Setup

# library(ggplot2)
# library(reshape2)
library(viridis)
library(colorRamps)
library(gridExtra)
library(ggplot2)
library('igraph')
library(ggnet)
library(network)
library(khroma)
library(dplyr)

source('similarity.R')

sunset <- colour("sunset")
discrete_rainbow <- colour("discrete rainbow")

path.base = '../../../'
path.work = paste(path.base, '02_analysis/04_sv/01_data/', sep = '')
path.figures = paste(path.base, '02_analysis/04_sv/03_figures/', sep = '')
path.svs = paste(path.base, '01_data_common/02_annot_denovo/02_pannagram/svs/', sep = '')
# path.genes = paste(path.base, '01_data_common/02_annot_denovo/02_pannagram/genes/', sep = '')

# sim.cutoff = 0.9

sim.cutoff = 0.85

TEs


# Load similarity function

bl.file = paste(path.work,'new_te_on_te.fasta',sep = '')
bl.res = read.table(bl.file)
bl.res = bl.res[bl.res$V1 != bl.res$V8,]

bl.res.init = bl.res
bl.res = bl.res[bl.res$V6 >= sim.cutoff * 100,]

res.nest = findNestedness(bl.res, use.strand = F)
[1] 130447
[1] 17626
[1] 3789
[1] 1186
[1] 407
[1] 180
[1] 79
[1] 54
[1] 34
[1] 26
[1] 17
[1] 10
[1] 3
[1] 1
[1] 0
[1] 124919
[1] 17576
[1] 3842
[1] 1240
[1] 437
[1] 189
[1] 89
[1] 61
[1] 41
[1] 28
[1] 21
[1] 11
[1] 6
[1] 2
[1] 0
res.nest.len = sapply(unique(c(res.nest$V1, res.nest$V8)), function(s) as.numeric(strsplit(s, '\\|')[[1]][5]))
  
res.nest$len1 = res.nest.len[res.nest$V1]
res.nest$len8 = res.nest.len[res.nest$V8]
res.nest$p1 = res.nest$C1 / res.nest$len1
res.nest$p8 = res.nest$C8 / res.nest$len8

res.nest.sim = res.nest[(res.nest$p1 >= sim.cutoff) | 
                          (res.nest$p8 >= sim.cutoff),]

How many TEs are in the graph

Distribution among families and subfamilies Distribution among lengths

te.in.graph = unique(c(res.nest$V1, res.nest$V8))

# What is the actual number of TEs
file.content <- readLines(bl.file)

selected.lines <- file.content[grepl("^# Query:|hits found", file.content)]
df.query = data.frame(b.query=selected.lines[seq(1, length(selected.lines), by = 2)],
                      b.hits=selected.lines[seq(2, length(selected.lines), by = 2)])

df.query$query  <- gsub("^# Query: (.*)", "\\1", df.query$b.query)
df.query$len <- as.numeric(sapply(strsplit(df.query$query, "\\|"), function(x) x[5]))
df.query$hits <- as.numeric(stringr::str_extract(df.query$b.hits, "\\d+"))
df.query$val.hits = df.query$hits
df.query$val.hits[df.query$val.hits >= 2] = 2
df.query$val.hits[df.query$query %in% bl.res$V8] = 2
df.query$val.hits[df.query$query %in% te.in.graph] = 3
hit.values = c('0 hits', '1 self-hit', 'partial overlap', 'in graph', "in graph but not in SVs")
df.query$s.hits = hit.values[df.query$val.hits+1]
df.query$s.hits = factor(df.query$s.hits, levels = rev(hit.values))
df.query$family <- sapply(strsplit(df.query$query, "\\|"), function(x) x[9])
df.query$subfam <- sapply(strsplit(df.query$query, "\\|"), function(x) x[8])


my_colors <- colors <- c("in graph" = "#676FA3",
            "partial overlap" = "#FF9F29",
            "1 self-hit" = "#6EBF8B",
            "0 hits" = "#D82148",
            "in graph but not in SVs" = "#151D3B")


# TEs, which are not in SVs
te.in.svs = read.table(paste(path.work, 'blast_tes_on_sv.txt', sep = ''), stringsAsFactors = F)
te.rest = setdiff(df.query$query, te.in.svs$V1)
te.in.svs = read.table(paste(path.work, 'blast_sv_on_tes.txt', sep = ''), stringsAsFactors = F)
te.rest = setdiff(te.rest, te.in.svs$V8)
df.query$s.hits[df.query$query %in% te.rest] = "in graph but not in SVs"


p = ggplot(df.query, aes(x = len, fill = s.hits, color = s.hits)) +
  # geom_histogram(aes(y = ..density..), alpha=0.5, color = "black", bins = 30) +
  # geom_jitter(height = 0.02, width = 0, alpha = 0.7) +
  geom_density(alpha = 0.5) +
  scale_fill_manual(values = my_colors) +
  scale_color_manual(values = my_colors) +
   scale_x_log10() +
  labs(fill = NULL, color = NULL) +
  xlab('length of TEs') + ylab('Normalised density') +
  theme_minimal() +
  theme(legend.position = c(1, 1), legend.justification = c(1, 1),
          legend.background = element_rect(color = "grey90"))

p

pdf(paste(path.figures, 'tes_self_blast_len_density.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

table(df.query$val.hits)

    0     1     2     3 
 1584 13615   766 19125 

TEs not in SVs

# TEs in te-graph: te.in.graph
# TEs which are have no connection to SVs

df = as.data.frame(table(df.query$s.hits))

  
colors <- c("in graph" = "#676FA3",
            "partial overlap" = "#FF9F29",
            "1 self-hit" = "#6EBF8B",
            "0 hits" = "#D82148",
            "in graph but not in SVs" = "#151D3B")


p = ggplot(df, aes(x = "", y = Freq, fill = Var1)) +
  geom_bar(stat="identity", width=1, alpha = 0.7) +
  coord_polar("y", start=0) +
  labs(title=NULL, fill="Categories") +
  theme_void()+
    scale_fill_manual(values = colors) +
  geom_text(aes(label = Freq,x = 1.3), position = position_stack(vjust = 0.5)) + theme(legend.position="none")
p


pdf(paste(path.figures, 'tes_self_blast_pie_chart.pdf', sep = ''), width = 3, height = 3)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Examples

Examples no hits


pdf(paste(path.figures, 'tes_self_scatter_no_hits_long.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 

head(df.query.tmp[df.query.tmp$family == 'DNA/MuDR',]$query)
[1] "te|641277|641420|1|144|+|AT1TE02080|ARNOLDY1|DNA/MuDR"     
[2] "te|1157763|1157863|1|101|+|AT1TE03780|LIMPET1|DNA/MuDR"    
[3] "te|4546279|4546388|1|110|+|AT1TE14750|ARNOLD2|DNA/MuDR"    
[4] "te|6753991|6754119|1|129|-|AT1TE21830|ATDNAI27T9A|DNA/MuDR"
[5] "te|10655834|10655963|1|130|+|AT1TE34455|ARNOLD1|DNA/MuDR"  
[6] "te|11660991|11661122|1|132|+|AT1TE37760|ATDNA2T9C|DNA/MuDR"

Examples one self-hit

families



df.query.tmp = df.query[(df.query$val.hits == 1),]

cnt.init = c(table(df.query$family))
cnt.tmp = c(table(df.query.tmp$family))

common_names <- intersect(names(cnt.init), names(cnt.tmp))
# Создание dataframe только для совпадающих имен
df_match <- data.frame(names = common_names, values.init = cnt.init[common_names], 
                       values.tmp = cnt.tmp[common_names])


gradient_colors <- c(discrete_rainbow(nrow(df_match)))
names(gradient_colors) = NULL


p = ggplot(df_match, aes(x = values.init, y = values.tmp, label = names, color = names)) +
  geom_point() +
  # geom_text(hjust = 0, vjust = 0) +
  ggrepel::geom_text_repel(max.overlaps = 20) +
  xlab("Initial counts") +
  ylab("Counts in \"1 self-hits\" category") +
  scale_x_log10() +
  scale_y_log10() +
  scale_color_manual(values = gradient_colors) +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
p


pdf(paste(path.figures, 'tes_self_scatter_1_selfhits_fam.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

subfamilies



df.query.tmp = df.query[(df.query$val.hits == 1) & (df.query$len >= 600),]

cnt.init = c(table(df.query$subfam))
cnt.tmp = c(table(df.query.tmp$subfam))

common_names <- intersect(names(cnt.init), names(cnt.tmp))
# Создание dataframe только для совпадающих имен
df_match <- data.frame(names = common_names, values.init = cnt.init[common_names], 
                       values.tmp = cnt.tmp[common_names])


# gradient_colors <- c(discrete_rainbow(nrow(df_match)))
names(gradient_colors) = NULL


p = ggplot(df_match, aes(x = values.init, y = values.tmp, label = names, color = names)) +
  geom_point() +
  # geom_text(hjust = 0, vjust = 0) +
  ggrepel::geom_text_repel(max.overlaps = 20) +
  xlab("Initial counts") +
  ylab("Counts in \"1 self-hits\" category") +
  scale_x_log10() +
  scale_y_log10() +
  # scale_color_manual(values = gradient_colors) +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
p


pdf(paste(path.figures, 'tes_self_scatter_1_selfhits_subfam.pdf', sep = ''), width = 7, height = 5)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

individuals from subfamilies

s.subfam = 'ATREP8'

df.query.tmp = df.query[(df.query$subfam == s.subfam) & (df.query$len >= 600),]
df.query.tmp

Creating the graph

# all edges
idx = res.nest$p1 >= sim.cutoff
edges = cbind(res.nest$V1[idx], res.nest$V8[idx])
idx = res.nest$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest$V8[idx], res.nest$V1[idx]))
te.enges.names = unique(c(edges[,1], edges[,2]))
te.enges.fam = sapply(te.enges.names, function(s) strsplit(s, '\\|')[[1]][9] )

te.enges.fam[te.enges.fam %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
te.enges.fam[te.enges.fam %in% c('RathE1_cons', 'RathE2_cons', 'RathE3_cons')] = 'RathE1/2/3_cons'
te.enges.fam[te.enges.fam %in% c('LINE/L1', 'LINE?')] = 'LINE'
te.enges.fam[te.enges.fam %in% c('Unassigned')] = 'Mix'
te.enges.fam[te.enges.fam %in% c('RC/Helitron')] = 'Helitron'

edges = edges[te.enges.fam[edges[,1]] != 'TEG',]
edges = edges[te.enges.fam[edges[,2]] != 'TEG',]
te.enges.names = unique(c(edges[,1], edges[,2]))


# nodes
idx = (res.nest$p1 >= sim.cutoff) & (res.nest$p8 >= sim.cutoff)
te.nodes = cbind(res.nest$V1[idx], res.nest$V8[idx])
te.nodes = te.nodes[te.enges.fam[te.nodes[,1]] != 'TEG',]
te.nodes = te.nodes[te.enges.fam[te.nodes[,2]] != 'TEG',]

te.rest = setdiff(te.enges.names, c(te.nodes[,1], te.nodes[,2]))


te.nodes.graph <- igraph::make_graph(t(te.nodes), directed = T)
te.nodes.graph <- igraph::simplify(te.nodes.graph)
te.nodes.comp <- igraph::components(te.nodes.graph)

nodes = data.frame(node = paste('N', te.nodes.comp$membership, sep = ''), 
                   te = names(te.nodes.comp$membership))

nodes.rest = data.frame(node = paste('R', (1:length(te.rest)), sep = ''), te = te.rest)
nodes = rbind(nodes, nodes.rest)

rownames(nodes) = nodes$te


nodes.cnt = data.frame(cnt = c(table(nodes$node)))
nodes.cnt$node = rownames(nodes.cnt)
nodes.cnt$fam = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  fam.te = unique(te.enges.fam[s.te])
  if(length(fam.te) == 1){
    return(fam.te)
  } else {
    fam.te = setdiff(fam.te, 'TEG')
    if(length(fam.te) == 1) return(fam.te)
    return('Mix')
  }
})
table(nodes.cnt$fam)

            DNA        DNA/MuDR        Helitron            LINE       LTR/Copia       LTR/Gypsy             Mix 
           1109            1228            2302             356             503            1837              67 
RathE1/2/3_cons            SINE 
             53              27 
# Redefine edges but with node names
idx.endes = (edges[,1] %in% nodes$te) & (edges[,2] %in% nodes$te)
b.graph = cbind(nodes[edges[idx.endes,1], 'node'],nodes[edges[idx.endes,2], 'node'])
b.graph = unique(b.graph)
# b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph.uni = b.graph[b.graph[,1] == b.graph[,2],]
b.graph = b.graph[b.graph[,1] != b.graph[,2],]

length(unique(c(b.graph[,1], b.graph[,2])))
[1] 7245
# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
[1] 1000
[1] 2000
[1] 3000
[1] 4000
[1] 5000
[1] 6000
[1] 7000
[1] 8000
[1] 9000
[1] 10000
[1] 11000
[1] 12000
[1] 13000
[1] 14000
[1] 15000
[1] 16000
[1] 17000
[1] 18000
[1] 19000
[1] 20000
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]
# b.graph = rbind(b.graph, b.graph.uni)

# Print graph

g.nodes.fam = nodes.cnt$fam
names(g.nodes.fam) = nodes.cnt$node
g.nodes.cnt = nodes.cnt$cnt
names(g.nodes.cnt) = nodes.cnt$node

g.cols = discrete_rainbow(length(unique(g.nodes.fam)))
names(g.cols) = unique(g.nodes.fam)

b.graph.init = b.graph


g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

Old colors

p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.fam[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) 
Loading required package: sna
Loading required package: statnet.common

Attaching package: ‘statnet.common’

The following objects are masked from ‘package:base’:

    attr, order

sna: Tools for Social Network Analysis
Version 2.7-1 created on 2023-01-24.
copyright (c) 2005, Carter T. Butts, University of California-Irvine
 For citation information, type citation("sna").
 Type help(package="sna") to get started.


Attaching package: ‘sna’

The following objects are masked from ‘package:igraph’:

    betweenness, bonpow, closeness, components, degree, dyad.census, evcent, hierarchy, is.connected,
    neighborhood, triad.census

Loading required package: scales

Attaching package: ‘scales’

The following object is masked from ‘package:viridis’:

    viridis_pal

Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'
p + guides(size = F)


# 
# b.graph.fam = cbind(g.nodes.fam[b.graph[,1]], g.nodes.fam[b.graph[,2]])
# b.graph.fam
# 
# which((b.graph.fam[,1] == 'DNA/MuDR') & (b.graph.fam[,1] == 'LINE'))

New Family colors

g.fam.names = sort(unique(g.nodes.fam))
fam.palette = c()
idx.pallete = c()

idx.fam <- grep("^Helitron", g.fam.names, value = FALSE)
tmp.palette <- colorRampPalette(c('#BFACE2', '#266D98', '#422B72'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

idx.fam <- grep("^LTR", g.fam.names, value = FALSE)
tmp.palette <- colorRampPalette(c('#BFDB38', '#54B435'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

idx.fam <- grep("^DNA", g.fam.names, value = FALSE)
tmp.palette <- colorRampPalette(c('#F9B5D0', '#971549'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

idx.fam = setdiff(1:length(g.fam.names), idx.pallete)
tmp.palette <- colorRampPalette(c('#FFC26F', '#C38154', '#884A39', '#4E3636'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

names(fam.palette) = g.fam.names[idx.pallete]
fam.palette['Unassigned'] = 'grey'
fam.palette['Mix'] = 'black'
fam.palette['TEG'] = 'darkgreen'

Separately visualise connected components

tmp.graph <- igraph::make_graph(t(b.graph), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

tmp.cnt = table(tmp.comp$membership)
tmp.cnt = -sort(-tmp.cnt)
head(tmp.cnt)

   2   51  209  176  104   29 
3493   40   38   37   32   31 
k = 1
tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.big <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.big = network.vertex.names(g.part.sub.big)


set.seed(20)
p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'
p.big.type = p + theme(legend.position = "none")

# set.seed(20)
# p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
#             node.size = g.nodes.cnt[b.graph.names.sub.big], 
#             color = g.nodes.fam[b.graph.names.sub.big],
#             mode = 'kamadakawai',
#             palette = fam.palette) + guides(size = F)
# p.big.color = p + theme(legend.position = "none")


tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership != tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.small <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.small = network.vertex.names(g.part.sub.small)


set.seed(20)
p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.small], 
            color = g.nodes.fam[b.graph.names.sub.small],
            # mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'
p.small.type =p + theme(legend.position = "none")

# set.seed(20)
# p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
#             node.size = g.nodes.cnt[b.graph.names.sub.small], 
#             color = g.nodes.fam[b.graph.names.sub.small],
#             # mode = 'kamadakawai',
#             palette = fam.palette) + guides(size = F)
# p.small.color = p + theme(legend.position = "none")

Plots

p.big.type

p.small.type


pdf(paste(path.figures, 'graph_tes_family_small.pdf', sep = ''), width = 9, height = 9)
print(p.small.type)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 
pdf(paste(path.figures, 'graph_tes_family_big.pdf', sep = ''), width = 5, height = 5)
print(p.big.type)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Stop for the paper

stop()

Specific TE families

Graph of one family

sort(-table(df.query$subfam[(df.query$val.hits == 3) & (df.query$family == 'LTR/Copia')]))

     META1  ATCOPIA95  ATCOPIA57  ATCOPIA28  ATCOPIA41  ATCOPIA49  ROMANIAT5  ATCOPIA94  ATCOPIA13  ATCOPIA37 
       -58        -51        -34        -33        -28        -28        -28        -27        -23        -22 
 ATCOPIA65  ATCOPIA43  ATCOPIA35  ATCOPIA42  ATCOPIA69  ATCOPIA78  ATCOPIA27  ATCOPIA58      ATRE1  ATCOPIA29 
       -22        -21        -16        -15        -15        -15        -14        -14        -14        -13 
 ATCOPIA54  ATCOPIA45  ATCOPIA51  ATCOPIA66  ATCOPIA75  ATCOPIA12  ATCOPIA36  ATCOPIA50  ATCOPIA67   ENDOVIR1 
       -12        -11        -11        -11        -11        -10        -10        -10        -10        -10 
 ATCOPIA16  ATCOPIA21  ATCOPIA22  ATCOPIA34   ATCOPIA4  ATCOPIA44  ATCOPIA48  ATCOPIA62  ATCOPIA63  ATCOPIA64 
        -9         -9         -9         -9         -9         -9         -9         -9         -9         -9 
 ATCOPIA87  ATCOPIA11  ATCOPIA55  ATCOPIA61  ATCOPIA70  ATCOPIA15  ATCOPIA25  ATCOPIA30  ATCOPIA52  ATCOPIA93 
        -9         -8         -8         -8         -8         -7         -7         -7         -7         -7 
 ATCOPIA96  ATCOPIA26  ATCOPIA31  ATCOPIA56  ATCOPIA8A   ATCOPIA9   ATCOPIA1  ATCOPIA10  ATCOPIA23   ATCOPIA3 
        -7         -6         -6         -6         -6         -6         -5         -5         -5         -5 
ATCOPIA31A  ATCOPIA38  ATCOPIA40   ATCOPIA5  ATCOPIA83  ATCOPIA89  ATCOPIA91  ATCOPIA97  ATCOPIA14  ATCOPIA17 
        -5         -5         -5         -5         -5         -5         -5         -5         -4         -4 
  ATCOPIA2  ATCOPIA24  ATCOPIA32  ATCOPIA33 ATCOPIA38B  ATCOPIA39  ATCOPIA46  ATCOPIA68  ATCOPIA74  ATCOPIA88 
        -4         -4         -4         -4         -4         -4         -4         -4         -4         -4 
 ATCOPIA8B  ATCOPIA90  ATCOPIA19  ATCOPIA60  ATCOPIA72  ATCOPIA76  ATCOPIA77  ATCOPIA79  ATCOPIA82  ATCOPIA85 
        -4         -4         -3         -3         -3         -3         -3         -3         -3         -3 
 ATCOPIA86      TA1-2  ATCOPIA18 ATCOPIA18A  ATCOPIA20 ATCOPIA32B  ATCOPIA47  ATCOPIA53  ATCOPIA59 ATCOPIA65A 
        -3         -3         -2         -2         -2         -2         -2         -2         -2         -2 
 ATCOPIA71  ATCOPIA73  ATCOPIA81  ATCOPIA92 ATCOPIA38A   ATCOPIA6   ATCOPIA7  ATCOPIA80  ATCOPIA84 
        -2         -2         -2         -2         -1         -1         -1         -1         -1 

# one.te.fam = 'BRODYAGA1'
# one.te.fam = 'BRODYAGA2'
# one.te.fam = 'HELITRONY1D'
# one.te.fam = 'HELITRONY3'
one.te.fam = 'ATCOPIA41'
query.fam = df.query$query[df.query$subfam == one.te.fam]


one.te.fam = 'ATCOPIA41'
query.fam = df.query$query[df.query$subfam == one.te.fam]

res.nest.famp = res.nest[(res.nest$V1 %in% query.fam) | (res.nest$V8 %in% query.fam),]


idx = res.nest.famp$p1 >= sim.cutoff
edges = cbind(res.nest.famp$V1[idx], res.nest.famp$V8[idx])
idx = res.nest.famp$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest.famp$V8[idx], res.nest.famp$V1[idx]))


te.enges.names = unique(c(edges[,1], edges[,2]))
te.enges.fam = sapply(te.enges.names, function(s) strsplit(s, '\\|')[[1]][9] )
te.enges.fam[te.enges.fam %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
te.enges.fam[te.enges.fam %in% c('RathE1_cons', 'RathE2_cons', 'RathE3_cons')] = 'RathE1/2/3_cons'
te.enges.fam[te.enges.fam %in% c('LINE/L1', 'LINE?')] = 'LINE'
te.enges.fam[te.enges.fam %in% c('Unassigned')] = 'Mix'
te.enges.fam[te.enges.fam %in% c('RC/Helitron')] = 'Helitron'

g.part <- network(edges, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)
b.graph.len = as.numeric(sapply(strsplit(b.graph.names, "\\|"), function(x) x[5]))


label.family = sapply(strsplit(b.graph.names, "\\|"), function(x) x[8])
lab.cols = c('#3F2E3E', "white")
label.color = lab.cols[(label.family == one.te.fam) + 1]

set.seed(20)
p <- ggnet2(g.part, label = b.graph.len, edge.color = "black", 
             node.size = 15,
            alpha=0.8,
            arrow.gap = 0.015,
            arrow.size = 5,
            label.color = label.color,
            # node.size = g.nodes.cnt[b.graph.names], 
            color = te.enges.fam[b.graph.names],
            palette = fam.palette,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 

pdf(paste(path.figures, 'real_tes_subfam_', one.te.fam, '.pdf', sep = ''), width = 20, height = 18)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

set.seed(20)
p <- ggnet2(g.part, label = b.graph.names, edge.color = "black",
             node.size = 15,
            alpha=0.8,
            arrow.gap = 0.015,
            arrow.size = 5,
            # label.color = label.color,
            # node.size = g.nodes.cnt[b.graph.names],
            color = te.enges.fam[b.graph.names],
            palette = fam.palette,
            # mode = "kamadakawai"
            ) + guides(size = F)

pdf(paste(path.figures, 'real_tes_subfam_', one.te.fam, '_names.pdf', sep = ''), width = 50, height = 49)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

Dotplots

Functions



seq2mx <- function(seq, wsize){
  
  num_rows <- length(seq) - wsize + 1
  matrix_seq <- matrix(nrow = num_rows, ncol = wsize)
  for (i in 1:num_rows) {
    matrix_seq[i, ] <- seq[i:(i + wsize - 1)]
  }

  return(matrix_seq)
}

mxComp <- function(mx1, mx2, wsize, nmatch){
  mx.res = 0
  for(s in c('A', 'C', 'G', 'T')){
    mx.res = mx.res + (mx1 == s) %*% t(mx2 == s)
  }
  # mx.res = (mx.res >= nmatch) * 1
  mx.res[mx.res < nmatch] = 0
  
  indices <- which(mx.res != 0, arr.ind = TRUE)
  values <- mx.res[indices]
  result <- cbind(indices, values)
  result = as.data.frame(result)
  return(result)
}

dotplot <- function(seq1, seq2, wsize, nmatch) {
  seq2.rc = rev(seqinr::comp(seq2))

  mx1 = toupper(seq2mx(seq1, wsize))
  mx2 = toupper(seq2mx(seq2, wsize))
  
  result = mxComp(mx1, mx2, wsize, nmatch)
  
  mx2.rc = toupper(seq2mx(seq2.rc, wsize))
  result.rc = mxComp(mx1, mx2.rc, wsize, nmatch)
  result.rc$values = -result.rc$values
  result.rc$col = length(seq2) - result.rc$col - wsize + 2
  result = rbind(result.rc, result)
  
  
  p = ggplot(result, aes(x = row, y = col, fill = values)) +
    geom_tile(width = 1, height = 1) +
    # xlab(name1) + ylab(name2) +
    # xlab(paste0(strsplit(name1, '\\|')[[1]][7:9], collapse = '|')) + 
    # ylab(paste0(strsplit(name2, '\\|')[[1]][7:9], collapse = '|')) +
    # xlab('') + ylab('') +
    guides(fill = FALSE) +
    theme_minimal() + coord_fixed() +
    scale_x_continuous(expand = c(0, 0)) + 
    scale_y_continuous(expand = c(0, 0)) +
    scale_fill_gradient2(low = "#CE1F6A", mid = "white", high = "#27374D", midpoint = 0) +
    theme(panel.border = element_rect(colour = "grey", fill=NA, size=1))
  p 
  return(p)
}
file.te.fasta = '/Volumes/Samsung_T5/vienn/tair/new_filtration/new_te.fasta'
te.fasta = seqinr::read.fasta(file.te.fasta)
te.names = names(te.fasta)
te.fasta = seqinr::getSequence(te.fasta)
names(te.fasta) = te.names

One pairwise example

wsize = 10
nmatch = 8

seq1 = te.fasta[[b.graph.names[1]]]
seq2 = te.fasta[[b.graph.names[1]]]


name1 = 'te|12384763|12385262|4|500|+|AT4TE57580|BRODYAGA1A|DNA/MuDR'
name2 = 'te|13674917|13675271|1|355|+|AT1TE44760|BRODYAGA1|DNA/MuDR'

# name1 = 'te|10592111|10592664|1|554|-|AT1TE34265|BRODYAGA2|DNA/MuDR'
# name2 = 'te|8743238|8744263|4|1026|-|AT4TE39045|HELITRONY1D|RC/Helitron'

name1 = 'te|6283198|6284421|4|1224|-|AT4TE26710|ATREP15|RC/Helitron'
name2 = 'te|6283198|6284421|4|1224|-|AT4TE26710|ATREP15|RC/Helitron'

seq1 = te.fasta[[name1]]
seq2 = te.fasta[[name2]]

p = dotplot(seq1, seq2, wsize, nmatch)

p = p + annotate("text", x = -Inf, y = Inf, label = paste('wsize=',wsize,'\nnmatch=',nmatch, sep = ''), 
             hjust = -0.1, vjust = 1.1)

p

pdf(paste(path.figures, 'pairwise_','.pdf', sep = ''), width = 5, height = 5)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

one VS all


wsize = 10
nmatch = 8

name0 = 'te|11683565|11689821|3|6257|+|AT3TE48540|ATCOPIA95|LTR/Copia'
name0 = 'te|16691748|16695154|1|3407|−|AT1TE55070|ATCOPIA41|LTR/Copia'
name0 = gsub('−', "-", name0)


one.te.fam = strsplit(name0, '\\|')[[1]][8]
# one.te.fam = 'BRODYAGA2'
query.fam = df.query$query[df.query$subfam == one.te.fam]
query.fam = query.fam[(query.fam %in% res.nest.sim$V1) | (query.fam %in% res.nest.sim$V2)]

names.all = setdiff(query.fam, name0)

p.all = list()
for(name2 in names.all){
  # message(name2)
  seq1 = te.fasta[[name0]]
  seq2 = te.fasta[[name2]]
  
  s1 = strsplit(name0, '\\|')[[1]][7]
  s2 = strsplit(name2, '\\|')[[1]][7]
  p = dotplot(seq1, seq2, wsize, nmatch) + xlab(s1) + ylab(s2)
  p.all[[name2]] = p
}
# 
# pp = grid.arrange(grobs = p.all, ncol = 13) ## display plot
# 
# 
# pdf(paste(path.figures, 'pairwise_all','.pdf', sep = ''), width = 50, height = 50)
# print(pp)     # Plot 1 --> in the first page of PDF
# dev.off()

s0 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '_')
s0 = gsub("/", "-", s0)
pdf(paste(path.figures, 'pairwise_all_',s0,'.pdf', sep = ''), width = 50, height = 50)
grid.arrange(grobs = p.all, ncol = ceiling(sqrt(length(p.all)))) # Write the grid.arrange in the file
dev.off() # Close the file

one connected component


wsize = 10
nmatch = 8


name0 = 'te|6205621|6206184|2|564|−|AT2TE25255|HELITRONY1D|RC/Helitron'
name0 = 'te|14189256|14190266|5|1011|-|AT5TE50700|HELITRONY3|RC/Helitron'
name0 = 'te|12513239|12513824|1|586|+|AT1TE40725|ATHILA4A|LTR/Gypsy'
name0 = 'te|11647426|11648912|1|1487|+|AT1TE37705|ATREP7|RC/Helitron'
name0 = 'te|11683565|11689821|3|6257|+|AT3TE48540|ATCOPIA95|LTR/Copia'
name0 = gsub('−', "-", name0)


names.all = unique(c(res.nest.sim$V1[res.nest.sim$V8 == name0],
                     res.nest.sim$V8[res.nest.sim$V1 == name0]))
# names.all = unique(c(res.nest$V1[res.nest$V8 == name0], 
#                      res.nest$V8[res.nest$V1 == name0]))

p.all = list()
for(name2 in names.all){
  # message(name2)
  seq1 = te.fasta[[name0]]
  seq2 = te.fasta[[name2]]
  
  s1 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '|')
  s2 = paste0(strsplit(name2, '\\|')[[1]][7:9], collapse = '|')
  p = dotplot(seq1, seq2, wsize, nmatch) + xlab(s1) + ylab(s2)
  p.all[[name2]] = p
}


s0 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '_')
s0 = gsub("/", "-", s0)
pdf(paste(path.figures, 'pairwise_connect_',s0,'.pdf', sep = ''), width = 50, height = 50)
grid.arrange(grobs = p.all, ncol = ceiling(sqrt(length(p.all)))) # Write the grid.arrange in the file
dev.off() # Close the file


name1 = 'te|14189256|14190266|5|1011|-|AT5TE50700|HELITRONY3|RC/Helitron'
name2 = 'te|2162295|2162937|2|643|-|AT2TE09950|HELITRONY3|RC/Helitron'
name0 = name1

names.all = unique(c(res.nest$V1[res.nest$V8 == name0], res.nest$V8[res.nest$V1 == name0]))


names = c(name1, name2)
b.tmp = bl.res[(bl.res$V1 %in% names) & (bl.res$V8 %in% names),]

res.nest[(res.nest$V1 %in% names) & (res.nest$V8 %in% names), ]

SVs

Reading nestedness


# Load similarity function

file.nestedness = paste(path.work, 'sv_big_on_big_nest.rds', sep = '')


if(!file.exists(file.nestedness)){
  bl.file = paste(path.work, 'sv_big_on_big.txt', sep = '')
  bl.res = read.table(bl.file)
  bl.res = bl.res[bl.res$V1 != bl.res$V8,]

  res.nest = findNestedness(bl.res, use.strand = F)
    
  res.nest$len1 = res.nest.len[res.nest$V1]
  res.nest$len8 = res.nest.len[res.nest$V8]
  res.nest$p1 = res.nest$C1 / res.nest$len1
  res.nest$p8 = res.nest$C8 / res.nest$len8  
  saveRDS(res.nest, file.nestedness, compress = F)
} else {
  res.nest = readRDS(file.nestedness)
}

res.nest.len = sapply(unique(c(res.nest$V1, res.nest$V8)), 
                      function(s) as.numeric(strsplit(s, '\\|')[[1]][2]))
res.nest0 = res.nest

TE stat

res.nest = res.nest0

sv.se = readRDS(paste(path.svs, 'sv_se.rds', sep = ''))

sv.se.len = sv.se[sv.se$len >= 100,]
sv.se.len$in.connect = sv.se.len$name %in% names(res.nest.len)

cnt.sv.se = table(sv.se.len$in.connect , sv.se.len$te)
cnt.sv.se
       
        hasTE hasTEpart isTE isTEpart noTE
  FALSE   220       454   41      682 5615
  TRUE   2864      1468 4299     2627 2049
df = reshape2::melt(cnt.sv.se)

te.content.names = c("noTE", "isTE", "hasTE", "hasTEpart", "isTEpart")
cols = c('#D8D9CF', '#EB455F', '#7B6079', '#3C8DAD', '#79B773')
names(cols) = te.content.names

df$Var2 = factor(df$Var2, levels = rev(c('isTE', 'isTEpart', 'hasTE', 'hasTEpart', 'noTE')))


p = ggplot(df, aes(x = Var2, y = value, fill = Var2, alpha = Var1, color = Var1)) +
  geom_col(position = "dodge", width = 0.8) +
  scale_alpha_manual(values = c(0.8, 1), labels = c("No", "Yes")) +
  scale_color_manual(values = c('transparent', 'black'), labels = c("not in graph", "in graph")) +
  labs(fill = "", color='Connected to others') +
  scale_fill_manual(values = cols) +
  xlab("") +
  ylab("Number of SVs") +
  theme(axis.text.y = element_blank()) + 
  guides(alpha = "none", fill = 'none') +
  theme_minimal() + coord_flip() +
  theme(
    legend.position = c(0.7, 0.3),     # Adjust these coordinates as needed
    legend.background = element_rect(fill="transparent", color='grey70')  # Makes the legend background transparent
  ) +
  guides(color = guide_legend(override.aes = list(fill = "#AEC3AE")))  +
  theme(axis.text.y = element_blank())
p


pdf(paste(path.figures, 'graph_mob_in_graph.pdf', sep = ''), width = 3.5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

TE families in SV types

sv.se.len = sv.se[sv.se$len >= 100,]
cnt.fam.sv = table(sv.se.len$fam[sv.se.len$fam!=''], sv.se.len$te[sv.se.len$fam!=''])
cnt.fam.sv = t(apply(cnt.fam.sv, 1, function(x) x/sum(x)))
cnt.fam.sv = reshape2::melt(cnt.fam.sv)

p = ggplot(cnt.fam.sv, aes(x = Var2, y = Var1, color = Var2)) + 
  geom_point(aes(size = value, alpha = value * 2)) + theme_minimal() + 
  scale_color_manual(values = cols)  +
  geom_text(data = cnt.fam.sv[cnt.fam.sv$value >= 0.2,], 
              aes(x=Var2, y=Var1, label = round(value, 2)), 
              size = 2.5, color = 'black', 
            nudge_x = 0.3,
            nudge_y = 0) +
  guides(size = "none", alpha = "none", color = 'none') +
  xlab('SV type') + ylab('TE family')
p



cnt.fam.sv = rowSums(table(sv.se.len$fam[sv.se.len$fam!=''], sv.se.len$te[sv.se.len$fam!='']))
cnt.fam.sv = data.frame(value = cnt.fam.sv, names = names(cnt.fam.sv))
rownames(cnt.fam.sv) = NULL

g = ggplot(cnt.fam.sv, aes(x = names, y = value)) +
  geom_bar(stat="identity", fill="grey80")+
  coord_flip() + theme_minimal() + theme(axis.title.y = element_blank(),
                        axis.text.y = element_blank(),
                        axis.ticks.y = element_blank()) +
  scale_y_continuous(labels = paste("1e",seq(0,4,1), sep = ''), breaks= seq(0,4,1)*1000) +
  ylab('#') + geom_text(aes(label=value, y=0), hjust=0, size = 2.5)
g 



pp = ggpubr::ggarrange(p, g, ncol = 2, widths = c(0.75, 0.25))
pp

pdf(paste(path.figures, 'graph_mob_te_fam_sv_type.pdf', sep = ''), width = 6, height = 4)
print(pp)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

# Insertion and deletion
idx = (sv.se.len$fam!='') & (sv.se.len$freq.max <= 3)
table(sv.se.len$fam[idx], sv.se.len$te[idx])
                    
                     hasTE hasTEpart isTE isTEpart
  DNA/HAT               91        35  123       20
  DNA/MuDR             494       114  529      195
  DNA+                 263        57  323       78
  Helitron             395       354 1192      651
  Helitron/DNA/HAT      16         7  104        1
  Helitron/DNA/MuDR     58        29    0       11
  Helitron/DNA+         87        15    0        7
  Helitron/LINE          8         5    0        0
  Helitron/LTR/Copia     2         9    0        3
  Helitron/LTR/Gypsy     1         3    0        0
  LINE                 257       129   66      162
  LTR/Copia            216       201  753      180
  LTR/Gypsy            150        62  121      170
  Mix                  152        87    0       16
  RathE1/2/3_cons        8         9    8       12
  SINE                   3         2    1        0
  TEG                   23        72   72       95
  Unassigned            21        33   12       20
idx = (sv.se.len$fam!='') & (sv.se.len$freq.max >= 25) & (sv.se.len$len >= 100) 
table(sv.se.len$fam[idx], sv.se.len$te[idx])
                          
                           hasTE hasTEpart isTE isTEpart
  DNA/HAT                      5         3    7        5
  DNA/MuDR                    31        20   10      111
  DNA+                        25        13   10       55
  Helitron                    64       111   18      241
  Helitron/DNA/MuDR            4         8    0        5
  Helitron/DNA+                2         0    0        7
  Helitron/LINE                0         1    0        0
  Helitron/LTR/Copia           0         1    0        1
  Helitron/LTR/Gypsy           1         1    0        0
  Helitron/RathE1/2/3_cons     0         1    0        0
  Helitron/SINE                0         1    0        0
  LINE                        14        20    8       39
  LTR/Copia                   16         7    5       49
  LTR/Gypsy                   22        13    2      121
  Mix                          7        10    0       14
  RathE1/2/3_cons              7         2    1        1
  SINE                         2         3    1        1
  TEG                          2        12    2       36
  Unassigned                   0         1    1        6

Filtration


res.nest = res.nest0

sv.names.mix = sv.se$name[sv.se$fam == 'Mix']
res.nest = res.nest[!(res.nest$V1 %in% sv.names.mix),]
res.nest = res.nest[!(res.nest$V8 %in% sv.names.mix),]


sv.names.mix = sv.se$name[sv.se$te == 'noTE']
res.nest = res.nest[!(res.nest$V1 %in% sv.names.mix),]
res.nest = res.nest[!(res.nest$V8 %in% sv.names.mix),]

singleton.mode = F
if(singleton.mode){
  sv.names.freq = sv.se$name[sv.se$freq.max <= 3]
  # sv.names.freq = sv.se$name[sv.se$freq.max >= 25]
  res.nest = res.nest[res.nest$V1 %in% sv.names.freq,]
  res.nest = res.nest[res.nest$V8 %in% sv.names.freq,]
}

prefix.mode = c('', '_single')

Graph

# all edges
idx = res.nest$p1 >= sim.cutoff
edges = cbind(res.nest$V1[idx], res.nest$V8[idx])
idx = res.nest$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest$V8[idx], res.nest$V1[idx]))
te.enges.names = unique(c(edges[,1], edges[,2]))

tmp = sv.se$te
names(tmp) = sv.se$name
te.enges.type = tmp[te.enges.names]

tmp = sv.se$fam
names(tmp) = sv.se$name
te.enges.fam = tmp[te.enges.names]

# nodes
idx = (res.nest$p1 >= sim.cutoff) & (res.nest$p8 >= sim.cutoff)
te.nodes = cbind(res.nest$V1[idx], res.nest$V8[idx])
te.rest = setdiff(te.enges.names, c(te.nodes[,1], te.nodes[,2]))


te.nodes.graph <- igraph::make_graph(t(te.nodes), directed = T)
te.nodes.graph <- igraph::simplify(te.nodes.graph)
te.nodes.comp <- igraph::components(te.nodes.graph)

nodes = data.frame(node = paste('N', te.nodes.comp$membership, sep = ''), te = names(te.nodes.comp$membership))

nodes.rest = data.frame(node = paste('R', (1:length(te.rest)), sep = ''), te = te.rest)
nodes = rbind(nodes, nodes.rest)

rownames(nodes) = nodes$te

# Define TE type
nodes.cnt = data.frame(cnt = c(table(nodes$node)))
nodes.cnt$node = rownames(nodes.cnt)
nodes.cnt$type = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  type.te = unique(te.enges.type[s.te])
  if(length(type.te) == 1){
    return(type.te)
  } else {
    type.te = table(type.te)
    type.te = names(type.te)[type.te == max(type.te)]
    return(type.te[1])
  }
})
table(nodes.cnt$type)

    hasTE hasTEpart      isTE  isTEpart 
     1116       488       437      1940 
# Define TE family
nodes.cnt$fam = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  type.te = unique(te.enges.fam[s.te])
  if(length(type.te) == 1){
    return(type.te)
  } else {
    type.te = table(type.te)
    type.te = names(type.te)[type.te == max(type.te)]
    return(type.te[1])
  }
})
table(nodes.cnt$fam)

           DNA/HAT           DNA/MuDR               DNA+           Helitron   Helitron/DNA/HAT  Helitron/DNA/MuDR 
               127                705                363                827                 16                 50 
     Helitron/DNA+      Helitron/LINE Helitron/LTR/Copia Helitron/LTR/Gypsy      Helitron/SINE               LINE 
                36                  9                 11                  4                  1                443 
         LTR/Copia          LTR/Gypsy    RathE1/2/3_cons               SINE                TEG         Unassigned 
               442                702                 30                 13                149                 53 
# Redefine edges but with node names
idx.endes = (edges[,1] %in% nodes$te) & (edges[,2] %in% nodes$te)
b.graph = cbind(nodes[edges[idx.endes,1], 'node'],nodes[edges[idx.endes,2], 'node'])
b.graph = unique(b.graph)
# b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph.uni = b.graph[b.graph[,1] == b.graph[,2],]
b.graph = b.graph[b.graph[,1] != b.graph[,2],]

length(unique(c(b.graph[,1], b.graph[,2])))
[1] 3782
# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
[1] 1000
[1] 2000
[1] 3000
[1] 4000
[1] 5000
[1] 6000
[1] 7000
[1] 8000
[1] 9000
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]
# b.graph = rbind(b.graph, b.graph.uni)

# Print graph

g.nodes.type = nodes.cnt$type
names(g.nodes.type) = nodes.cnt$node
g.nodes.cnt = nodes.cnt$cnt
names(g.nodes.cnt) = nodes.cnt$node
g.nodes.fam = nodes.cnt$fam
names(g.nodes.fam) = nodes.cnt$node


g.cols.names = c("noTE", "isTE", "hasTE", "hasTEpart", "isTEpart")
g.cols = c('#FFD966', '#EB455F', '#7B6079', '#3C8DAD', '#79B773')
names(g.cols) = g.cols.names


g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.type[b.graph.names],
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = g.cols) + guides(size = F)
Warning: 'length(x) = 3781 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3781 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3781 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3781 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3781 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3781 > 1' in coercion to 'logical(1)'
p

# path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_mob_all_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = 5, height = 5)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

# set.seed(20)
# p <- ggnet2(g.part, label = F, edge.color = "grey30", 
#             node.size = g.nodes.cnt[b.graph.names], 
#             color = c('TE', 'noTE')[(g.nodes.type[b.graph.names] == 'noTE')*1+1],
#             # mode = 'kamadakawai',
#             # arrow.gap = 0, 
#             # arrow.size = 3,
#             palette = c('noTE' = 'black', 'TE' = '#AEC3AE')) + guides(size = F)
# p
# 
# # path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
# pdf(paste(path.figures, 'graph_mob_all_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
#     width = 5, height = 5)
# print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
# dev.off()

Colored by TE family

pdf(paste(path.figures, 'graph_mob_cluster', prefix.mode[singleton.mode+1] ,'_family_legend.pdf', sep = ''), width = 7, height = 5)
print(p+ coord_fixed(ratio = 1))     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 

Node size distribution

df = data.frame(node = unique(nodes$node))
df$size = g.nodes.cnt[df$node]
df$fam = g.nodes.fam[df$node]
df$type = g.nodes.type[df$node]

fam.palette
          Helitron   Helitron/DNA/HAT  Helitron/DNA/MuDR      Helitron/DNA+      Helitron/LINE Helitron/LTR/Copia 
         "#BFACE2"          "#939ACC"          "#6788B7"          "#3B76A2"          "#2A6392"          "#325087" 
Helitron/LTR/Gypsy      Helitron/SINE          LTR/Copia          LTR/Gypsy            DNA/HAT           DNA/MuDR 
         "#3A3D7C"          "#422B72"          "#BFDB38"          "#54B435"          "#F9B5D0"          "#C8658C" 
              DNA+               LINE    RathE1/2/3_cons               SINE                TEG         Unassigned 
         "#971549"          "#FFC26F"          "#D2915A"          "#A56546"          "#794538"             "grey" 
               Mix 
           "black" 
p = ggplot(df, aes(x = type, y = size, color=fam)) +
  geom_jitter(width = 0.2) +
  labs(x = "Type", y = "Size") + 
  scale_y_continuous(trans = "log2") +
  scale_color_manual(values = fam.palette)+
  theme_minimal() +
  guides(color = guide_legend(ncol = 2)) +
  labs(color = "TE family") + xlab('') + ylab('Node size (Number of similar SVs)')
p


pdf(paste(path.figures, 'graph_mob_size_distribution.pdf', sep = ''), width = 6.5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Separately visualise connected components

tmp.graph <- igraph::make_graph(t(b.graph), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

tmp.cnt = table(tmp.comp$membership)
tmp.cnt = -sort(-tmp.cnt)
head(tmp.cnt)

   1   28   14   33  134   22 
1834   28   27   25   22   21 
k = 1
tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.big <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.big = network.vertex.names(g.part.sub.big)


set.seed(20)
p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.type[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = g.cols) + guides(size = F)
Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'
p.big.type = p + theme(legend.position = "none")

set.seed(20)
p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1834 > 1' in coercion to 'logical(1)'
p.big.color = p + theme(legend.position = "none")


tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership != tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.small <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.small = network.vertex.names(g.part.sub.small)


set.seed(20)
p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.small], 
            color = g.nodes.type[b.graph.names.sub.small],
            # mode = 'kamadakawai',
            palette = g.cols) + guides(size = F)
Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'
p.small.type =p + theme(legend.position = "none")

set.seed(20)
p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.small], 
            color = g.nodes.fam[b.graph.names.sub.small],
            # mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 1947 > 1' in coercion to 'logical(1)'
p.small.color = p + theme(legend.position = "none")

Save

pdf(paste(path.figures, 'graph_mob_small_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = 6, height = 6)
print(p.small.type)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 
pdf(paste(path.figures, 'graph_mob_small_cluster', prefix.mode[singleton.mode+1] ,'_family.pdf', sep = ''), 
    width = 6, height = 6)
print(p.small.color)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 

Run by accessions

path.figures.acc = '/Volumes/Samsung_T5/vienn/work_te/figures_tegraph_accessions/'
sv.bin = read.table('/Volumes/Samsung_T5/vienn/work_sv/svs_se_bin_v03.txt', stringsAsFactors = F, check.names = FALSE)
# acc = '10002'

for(acc in colnames(sv.bin)){
  sv.acc = rownames(sv.bin)[sv.bin[,acc] == 1]
  rownames(sv.se) = sv.se$gr
  sv.acc = sv.se[sv.acc, 'name']
  
  sv.acc = intersect(sv.acc, rownames(nodes))
  nodes.cnt.acc = table(nodes[sv.acc,'node'])
  
  
  sv.alpha = rep(0, length(b.graph.names))
  names(sv.alpha) = b.graph.names
  sv.alpha[names(sv.alpha) %in% names(nodes.cnt.acc)] = 1
  
  # set.seed(239)
  # p <- ggnet2(g.part, label = F, edge.color = "black", 
  #             node.size = g.nodes.cnt[b.graph.names], 
  #             color = g.nodes.fam[b.graph.names],
  #             alpha = sv.alpha,
  #             # mode = 'kamadakawai',
  #             # arrow.gap = 0, 
  #             # arrow.size = 3,
  #             palette = fam.palette) + guides(size = F) + theme(legend.position = "none")
  
  set.seed(20)
  p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.small], 
            color = g.nodes.fam[b.graph.names.sub.small],
            alpha = sv.alpha[b.graph.names.sub.small],
            # mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F) + theme(legend.position = "none")

  pdf(paste(path.figures.acc, 'graph_te', prefix.mode[singleton.mode+1] ,'_small_acc_',acc,'.pdf', sep = ''), width = 5, height = 5)
  print(p)     # Plot 1 --> in the first page of PDF
  dev.off()
  
  
  set.seed(20)
  p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            alpha = sv.alpha[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F) + theme(legend.position = "none")

  pdf(paste(path.figures.acc, 'graph_te', prefix.mode[singleton.mode+1] ,'_big_acc_',acc,'.pdf', sep = ''), width = 5, height = 5)
  print(p)     # Plot 1 --> in the first page of PDF
  dev.off()

}

p 
sv.annot = read.table('/Volumes/Samsung_T5/vienn/work_sv/svs_annotation_v03.txt', stringsAsFactors = F)
rownames(sv.annot) = sv.annot$gr
head(sv.annot)

sv.annot[extracted_values,]

Stop

stop()

Big TE-nodes

n.amount = 20

g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

size.big = g.nodes.cnt[b.graph.names]
alpha.big = rep(1, length(b.graph.names))
names(alpha.big) = b.graph.names
alpha.big[size.big < n.amount] = 0

sum(size.big >= n.amount)
[1] 25
set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = size.big, 
            color = g.nodes.fam[b.graph.names],
            alpha= alpha.big,
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = fam.palette) + guides(size = F) + guides(color = guide_legend(ncol = 2))
Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'
p

Which families specifically, and is the rate of insertion is different?

compare number of insertions with the total number of TE load


big.families = data.frame(node =  names(size.big)[size.big >= n.amount])
big.families$size = size.big[big.families$node]
big.families$fam = g.nodes.fam[big.families$node]
big.families = big.families[order(-big.families$size),]
rownames(big.families) = NULL

node.big = nodes[nodes$node %in% big.families$node,]

v = read.table(paste(path.work, 'blast_sv_on_tes.txt', sep = ''))
v = v[v$V1 %in% node.big$te,]


pos.len1 = 2
pos.len2 = 5
v1.len = sapply(unique(v$V1), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len1]))
v8.len = sapply(unique(v$V8), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len2]))
v.len = c(v1.len, v8.len)

v.sim = findNestedness(v, use.strand = F)
[1] 0
Error in `$<-.data.frame`(`*tmp*`, "overlap", value = 0) : 
  replacement has 1 row, data has 0

no-TE SV

Construct

sv.se = readRDS(paste(path.svs, 'sv_se.rds', sep = ''))
sim.cutoff = 0.85


sv.se.no.te = sv.se$name[(sv.se$te == 'noTE') & (sv.se$len > 50)]

bl.file = paste(path.work,'sv_big_on_big.txt', sep = '')
bl.sv = read.table(bl.file, stringsAsFactors = F)
bl.sv = bl.sv[bl.sv$V1 != bl.sv$V8,]

# remove having TEs
bl.sv = bl.sv[bl.sv$V1 %in% sv.se.no.te, ]
bl.sv = bl.sv[bl.sv$V8 %in% sv.se.no.te, ]

pos.len1 = 2
sv.len = sapply(unique(c(bl.sv$V1, bl.sv$V8)), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len1]))
bl.sv$len1 = sv.len[bl.sv$V1]
bl.sv$len8 = sv.len[bl.sv$V8]
max.len = 20000
bl.sv = bl.sv[(bl.sv$len1 <= max.len) & (bl.sv$len8 <= max.len),]
bl.sv$p1 = (bl.sv$V3 - bl.sv$V2 + 1) / bl.sv$len1
bl.sv$p8 = (abs(bl.sv$V5 - bl.sv$V4) + 1) / bl.sv$len8
bl.sv$comb = as.factor(paste(bl.sv$V1, bl.sv$V8, sep = '||'))

idx.mutual = (bl.sv$p1 >= sim.cutoff) & (bl.sv$p8 >= sim.cutoff)
# There is a big discussion in my head, whether it should be '&' or '|'
# If it's not ,utual, then maybe with something else it will construct a mutual relation, 
# so we should remain for the analysis of nestedness all partial inclusions
sv.mutual = bl.sv[idx.mutual, ]
v = bl.sv[!idx.mutual, ]
v = v[!(v$comb %in% sv.mutual$comb),]

# At some point it was a step to remain only those instances which are not "unique" in combinations
# but I think it's not correct here

sv.sim = findNestedness(v, use.strand = T)
[1] 437
[1] 12
[1] 1
[1] 0
[1] 440
[1] 11
[1] 1
[1] 0
sv.sim$p1 = sv.sim$C1 / sv.len[sv.sim$V1]
sv.sim$p8 = sv.sim$C8 / sv.len[sv.sim$V8]

# here  we should finally use '|', not '&'
sv.nested = sv.sim[(sv.sim$p1 >= sim.cutoff) | (sv.sim$p8 >= sim.cutoff) ,]

# Create pre-data for defining edges
common.names = intersect(colnames(sv.mutual), colnames(sv.nested))
sv.overall = rbind(sv.mutual[,common.names], sv.nested[,common.names])
sv.overall$group = (sv.overall$p1 >= sim.cutoff) * 1 + (sv.overall$p8 >= sim.cutoff) * 2
idx1 = sv.overall$group != 2  # V1 in V8
idx2 = sv.overall$group != 1  # V8 in V1


# Edges 
sv.edges = rbind(cbind(sv.overall$V1[idx1], sv.overall$V8[idx1]),
                 cbind(sv.overall$V8[idx2], sv.overall$V1[idx2]))


sv.graph <- igraph::make_graph(t(sv.edges), directed = T)
sv.graph <- igraph::simplify(sv.graph)
sv.graphcomp <- igraph::components(sv.graph)

sv.memb = data.frame(memb = sv.graphcomp$membership)
sv.memb$name = rownames(sv.memb)
rownames(sv.memb) = NULL
rownames(sv.se) = sv.se$name
sv.memb$te = sv.se[sv.memb$name, 'te']
sv.memb$cover = sv.se[sv.memb$name, 'cover'] / sv.se[sv.memb$name, 'len']
sv.memb$len = sv.len[sv.memb$name]

Plot all

g.part <- network(sv.edges, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = 1,
            # node.size = g.nodes.cnt[b.graph.names], 
            # color = g.nodes.type[b.graph.names],
            # palette = g.cols
            ) + guides(size = F)
p 

Plot with colors

p = p+ theme(legend.key.height = unit(0.5, "cm"))
p

pdf(paste(path.figures, 'graph_new_all.pdf', sep = ''), width = 6, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Types of the component


sv.graph <- igraph::make_graph(t(sv.edges), directed = T)
sv.graph <- igraph::simplify(sv.graph)
sv.graphcomp <- igraph::components(sv.graph)

sv.comp.member = sv.graphcomp$membership

s.tags = c("transpos","reverse","repeat","zinc", "receptor","defined prot", "undefined prot", 'no prot')
s.tags0 = rep('', length(s.tags))
s.tags0[1:4] = 'TE-like'
s.tags0[5:6] = 'Known Proteins'
s.tags0[7] = 'Undef. Proteins'
s.tags0[8] = 'No Proteins'
names(s.tags0) = s.tags

comp.tags = rep('', length(unique(sv.comp.member)))
for(s.tag in s.tags){
  tmp.tags = unique(sv.comp.member[names(g.nodes.prot)[g.nodes.prot == s.tag]])
  comp.tags[tmp.tags][comp.tags[tmp.tags] == ''] = s.tag
}
comp.tags[comp.tags == ''] = 'no prot'
comp.tags = data.frame(table(comp.tags))
colnames(comp.tags) = c('tag1', 'freq')
comp.tags$tag1 = factor(comp.tags$tag1, levels = s.tags)
comp.tags = comp.tags[order(comp.tags$tag1),]

comp.tags$tag0 = s.tags0[comp.tags$tag1]
comp.tags$tag0 = factor(comp.tags$tag0, levels = unique(s.tags0))

y.ticks = tapply(comp.tags$freq, comp.tags$tag0, sum)
y.ticks = y.ticks[!is.na(y.ticks)]

yy = sum(y.ticks) - cumsum(y.ticks) + y.ticks/2

comp.tags$ymin <- c(0, cumsum(comp.tags$freq)[-length(comp.tags$freq)])
comp.tags$ymax <- cumsum(comp.tags$freq)

x.step = rep(0, 8)
n.step = 10
x.step[c(5,7,8)] = n.step
x.step = cumsum(x.step)

comp.tags$ymin = comp.tags$ymin + x.step
comp.tags$ymax = comp.tags$ymax + x.step

y.min = tapply(comp.tags$ymin, comp.tags$tag0, min)
y.max = tapply(comp.tags$ymax, comp.tags$tag0, max)
y.val = (y.max + y.min) / 2
y.cnt = tapply(comp.tags$freq, comp.tags$tag0, sum)

df.text = data.frame(y.min = y.min, y.max = y.max, y.val = y.val, y.cnt = y.cnt, label = names(y.val))
df.text$angles <- 360 - (df.text$y.val / (max(comp.tags$ymax) + n.step)) * 360 
df.text$angles[2:3] = 180 + df.text$angles[2:3]

p = ggplot(comp.tags, aes(x = 0, y = freq, fill = tag1)) +
   geom_rect(aes(xmin = -0.5, xmax = 0.5, ymin = ymin, ymax = ymax)) +
   coord_polar("y", start = 0) +
   scale_fill_manual(values = g.cols.plus) + ylim(0, max(comp.tags$ymax) + n.step) +
   theme_void() + xlim(-1.5, 0.7) + 
   geom_text(data=df.text, aes(x = 0.7, y = y.val, label = paste(label, y.cnt, sep = ': ')), 
             angle = df.text$angles, inherit.aes = FALSE) +
  theme(legend.position="none")

p = p + annotate("text", x = -1.5, y = 0, label = paste('Total',sum(comp.tags$freq),'\n connected \ncomponents')) 

p

pdf(paste(path.figures, 'graph_new_pie_chart.pdf', sep = ''), width = 3.1, height = 3.1)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

I don’t know

sv.se$freq = sv.se$freq.max
n.cutoff = 3
n = 28
sv.se$sin = 'indel'
sv.se$sin[sv.se$freq >= (n - n.cutoff)] = 'deletion'
sv.se$sin[sv.se$freq <= n.cutoff] = 'insertion'


g.nodes.prot.sin = g.nodes.prot
g.nodes.prot.sin[names(g.nodes.prot.sin) %in% sv.se$name[sv.se$sin != 'insertion'] ] = 'na'
g.cols['na'] = 'white'




set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            color = g.nodes.prot.sin[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 

# 
# path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
# pdf(paste(path.figures, 'graph_sv_note_insertion.pdf', sep = ''), width = 6, height = 4)
# print(p)     # Plot 1 --> in the first page of PDF
# dev.off()


alpha.edta = rep(1, length(b.graph.names))
names(alpha.edta) = b.graph.names

sv.annot.adta = rowSums(sv.annot[,11:ncol(sv.annot)] > 0.7) > 0
sv.annot.adta = sv.annot.adta[sv.se$gr]
names(sv.annot.adta) = sv.se$name
sv.annot.adta = sv.annot.adta[sv.annot.adta]
alpha.edta[names(alpha.edta) %in% names(sv.annot.adta)] = 0


set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            alpha=1-alpha.edta,
            color = g.nodes.prot[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 


pdf(paste(path.figures, 'graph_mob_note_edta.pdf', sep = ''), width = 6, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_mob_note_edta_no_legend.pdf', sep = ''), width = 5, height = 5)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()

Plot with component ID



tmp.graph <- igraph::make_graph(t(sv.edges), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

size.limit = 5
comp.id = as.character(tmp.comp$membership)
names(comp.id) = names(tmp.comp$membership)
comp.id[tmp.comp$csize[tmp.comp$membership] < size.limit] = ''

names.te = names(g.nodes.prot)[g.nodes.prot %in% c('transpos', 'reverse')]

comp.id[!(names(comp.id) %in% names.te)] = ''

comp.id[duplicated(comp.id)] = ''


comp.remain = as.numeric(comp.id[comp.id != ''])
alpha = rep(0, length(b.graph.names))
names(alpha) = names(tmp.comp$membership)
alpha[tmp.comp$membership %in% comp.remain] = 1

set.seed(239)
p <- ggnet2(g.part, label = comp.id[b.graph.names], 
            label.color = "black",
            label.size = 3,
            edge.color = "grey", 
            alpha = alpha[b.graph.names],
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            color = g.nodes.prot[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 


path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_sv_note_numbers.pdf', sep = ''), width = 5, height = 5)
print(p + theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()



# Order of components
cnt = table(tmp.comp$membership[tmp.comp$membership %in% comp.remain])
cnt = cnt[order(-cnt)]

CNV


cnv = readRDS('/Volumes/Samsung_T5/vienn/work_sv/similar_cnv_sv_on_accessions_cum_0.9.rds')

Plot one specific network


path.figures.examples  = '/Volumes/Samsung_T5/vienn/work_te/examples/'

# 
# tmp.graph <- igraph::make_graph(t(sv.edges), directed = T)
# tmp.graph <- igraph::simplify(tmp.graph)
# tmp.comp <- igraph::components(tmp.graph)
# 
# tmp.cnt = table(tmp.comp$membership)
# tmp.cnt = -sort(-tmp.cnt)

tmp.cnt = cnt

for(k in 1:length(tmp.cnt)){
  tmp.k = as.numeric(names(tmp.cnt)[k])
  tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
  b.graph.sub = sv.edges[(sv.edges[,1] %in% tmp.names) & 
                          (sv.edges[,2] %in% tmp.names),]
  
  
  g.part.sub <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
  b.graph.names.sub = network.vertex.names(g.part.sub)
  
  
    
  b.graph.size.sub <- as.numeric(sub(".*\\|", "", b.graph.names.sub))
  names(b.graph.size.sub) = b.graph.names.sub
  # b.graph.size.sub = ceiling(log(b.graph.size.sub, 10))
  
  if((length(unique( g.nodes.prot[b.graph.names.sub])) == 1)){
    set.seed(20)
    p <- ggnet2(g.part.sub, label = b.graph.size.sub[b.graph.names.sub], edge.color = "black", 
                node.size = 15,
                arrow.gap = 0.07, arrow.size = 3,
                color = g.cols[g.nodes.prot[b.graph.names.sub][1]],
                ) + guides(size = F) +  ggtitle(paste('Component #', tmp.k))
    p
  } else {
    set.seed(20)
    p <- ggnet2(g.part.sub, label = b.graph.size.sub[b.graph.names.sub], edge.color = "black", 
                node.size = 15,
                arrow.gap = 0.07, arrow.size = 3,
                color = g.nodes.prot[b.graph.names.sub],
                palette = g.cols,
                ) + guides(size = F) +  ggtitle(paste('Component #', tmp.k))
    p
  }
  
 
  
  pdf(paste(path.figures.examples, 'graph_sv_example_',k,'_comp_',tmp.k,'.pdf', sep = ''), width = 5, height = 4)
  print(p + theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
  dev.off()
  
  # annotation
  annot.tmp = sv.prot[sv.prot$name %in% b.graph.names.sub,]
  # annot.tmp = annot.tmp[annot.tmp$transpos == 1,]
  
  write.table(annot.tmp, paste(path.figures.examples, 'graph_sv_example_',k,'_pblast.txt', sep = ''), 
              row.names = F, col.names = F, quote = F, sep = '\t')
  
  
  # if EDTA annotation exists
  sv.tmp = unique(c(b.graph.sub))
  sv.tmp.cut <- gsub("\\|.*", "", sv.tmp)
  sv.annot.tmp = sv.annot[sv.tmp.cut,]
  n.fix = 9
  sv.annot.tmp  = sv.annot.tmp[,c(1:n.fix,n.fix+which(colSums(sv.annot.tmp[,(n.fix+1):ncol(sv.annot.tmp)]) != 0))]
  rownames(sv.annot.tmp) = sv.tmp
    
  write.table(sv.annot.tmp, paste(path.figures.examples, 'graph_sv_example_',k,'_edta.txt', sep = ''), 
             row.names = F, quote = F, sep = '\t')
  
  # Copy0Number variation
  cnv.tmp = cnv[sv.tmp,]
  
  heatmap(cnv.tmp, col = colorRampPalette(c("white", "red"))(20))
  
}

Pie-chart of proteins

library(ggplot2)

data <- data.frame(
  type = c("no proteins", "TE-related", "Категория 2", "Категория 3", "Категория 4"),
  value = c(135, 63, 85, 133)
)

pie.chart <- ggplot(data, aes(x = "", y = value, fill = type)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y", start = 0) +
  theme_void()

pie.chart

Admixture groups

groups <- c(
  "germany",
  "south_sweden",
  "north_sweden",
  "south_sweden",
  "north_sweden",
  "germany",
  "western_europe",
  "central_europe",
  "italy_balkan_caucasus",
  "spain",
  "relict",
  "asia",
  "central_europe",
  "admixed",
  "spain",
  "relict",
  "italy_balkan_caucasus",
  "western_europe",
  "asia",
  "africa",
  "china",
  "china",
  "africa",
  "africa",
  "madeira",
  "madeira",
  "africa"
)

# Используем функцию table() для подсчета количества элементов в каждой группе
as.matrix(table(groups))

OLD

sunset <- colour("sunset")
discrete_rainbow <- colour("discrete rainbow")

file.te = '/Volumes/Samsung_T5/vienn/work/blast_tes_ann.txt'
sim.cutoff = 0.85
len.cutoff = 100

b = read.table(file.te, stringsAsFactors = F)
b = b[b$V1 != b$V8,]
b$len1 = as.numeric(sapply(b$V1, function(s) strsplit(s, '\\|')[[1]][7]))
b$len2 = as.numeric(sapply(b$V8, function(s) strsplit(s, '\\|')[[1]][7]))
b = b[b$len1 >= len.cutoff,]
b = b[b$len2 >= len.cutoff,]
b$comb = paste(b$V1, b$V8, sep = '^')

# Order positions in base
idx = b$V4 > b$V5
tmp = b[idx, 'V4']
b[idx, 'V4'] = b[idx, 'V5']
b[idx, 'V5'] = tmp

# --------------------------------------------------
# Get separately those, who has a unique coverage
comb.tbl = table(b$comb)
idx.uni = b$comb %in% names(comb.tbl)[comb.tbl == 1]
b.uni = b[idx.uni,]
b = b[!idx.uni,]

# This variable will be used later
b.uni$p1 = (b.uni$V3 - b.uni$V2 + 1) / b.uni$len1
b.uni$p2 = (b.uni$V5 - b.uni$V4 + 1) / b.uni$len2
b.uni = b.uni[(b.uni$p1 >= sim.cutoff) | (b.uni$p2 >= sim.cutoff),]

b.relations = data.frame(sub.te = b.uni$V1[b.uni$p1 >= sim.cutoff],
                         te = b.uni$V8[b.uni$p1 >= sim.cutoff], stringsAsFactors = F)
b.relations = rbind(b.relations,
                    data.frame(sub.te = b.uni$V8[b.uni$p2 >= sim.cutoff],
                               te = b.uni$V1[b.uni$p2 >= sim.cutoff], stringsAsFactors = F))
b.relations = unique(b.relations)

# --------------------------------------------------
# Min-max of the coverage to remove those, who are NOT in each other completely
b.cov = tapply(b$V2, b$comb, min)
b.cov = data.frame(comb = names(b.cov), V2 = b.cov)
b.cov$V3 = tapply(b$V3, b$comb, max)
b.cov$V4 = tapply(b$V4, b$comb, min)
b.cov$V5 = tapply(b$V5, b$comb, max)
b.cov$len1 = tapply(b$len1, b$comb, unique)
b.cov$len2 = tapply(b$len2, b$comb, unique)
b.cov$p1 = (b.cov$V3 - b.cov$V2 + 1) / b.cov$len1
b.cov$p2 = (b.cov$V5 - b.cov$V4 + 1) / b.cov$len2

comb.uncov = b.cov$comb[(b.cov$p1 < sim.cutoff) & (b.cov$p2 < sim.cutoff)]

b = b[!(b$comb %in% comb.uncov),]

# --------------------------------------------------
# Calculate the coverage directly for the first
b = b[order(b$V3),]
b = b[order(b$V2),]
b = b[order(b$comb),]

# Remove nested
idx = which((b$V3[-nrow(b)] > b$V3[-1]) & (b$comb[-nrow(b)] == b$comb[-1])) + 1
b1 = b[-idx,]

# Compute gaps
b1$gap = c(b1$V2[-1] - b1$V3[-nrow(b1)] - 1, 0)
b1$gap[b1$gap < 0] = 0
idx.diff.comb = which(b1$comb[-1] != b1$comb[-nrow(b1)])
b1$gap[idx.diff.comb] = 0

b.cov = tapply(b1$V2, b1$comb, min)
b.cov = data.frame(comb = names(b.cov), V2 = b.cov)
b.cov$V3 = tapply(b1$V3, b1$comb, max)
b.cov$len1 = tapply(b1$len1, b1$comb, unique)
b.cov$gap = tapply(b1$gap, b1$comb, sum)
b.cov$len1 = b.cov$len1 
b.cov$p1 = (b.cov$V3 - b.cov$V2 + 1 - b.cov$gap) / b.cov$len1
b.cov$V1 = tapply(b1$V1, b1$comb, unique)
b.cov$V8 = tapply(b1$V8, b1$comb, unique)

b.cov = b.cov[b.cov$p1 >= sim.cutoff,]


b.relations = rbind(b.relations,
                    data.frame(sub.te = b.cov$V1,
                               te = b.cov$V8, stringsAsFactors = F))


# --------------------------------------------------
# Calculate the coverage directly for the second
b = b[order(b$V5),]
b = b[order(b$V4),]
b = b[order(b$comb),]

# Remove nested
idx = which((b$V5[-nrow(b)] > b$V5[-1]) & (b$comb[-nrow(b)] == b$comb[-1])) + 1
b1 = b[-idx,]

# Compute gaps
b1$gap = c(b1$V4[-1] - b1$V5[-nrow(b1)] - 1, 0)
b1$gap[b1$gap < 0] = 0
idx.diff.comb = which(b1$comb[-1] != b1$comb[-nrow(b1)])
b1$gap[idx.diff.comb] = 0

b.cov = tapply(b1$V4, b1$comb, min)
b.cov = data.frame(comb = names(b.cov), V4 = b.cov)
b.cov$V5 = tapply(b1$V5, b1$comb, max)
b.cov$len2 = tapply(b1$len2, b1$comb, unique)
b.cov$gap = tapply(b1$gap, b1$comb, sum)
b.cov$len2 = b.cov$len2 
b.cov$p1 = (b.cov$V5 - b.cov$V4 + 1 - b.cov$gap) / b.cov$len2
b.cov$V1 = tapply(b1$V1, b1$comb, unique)
b.cov$V8 = tapply(b1$V8, b1$comb, unique)

b.cov = b.cov[b.cov$p1 >= sim.cutoff,]


b.relations = rbind(b.relations,
                    data.frame(sub.te = b.cov$V8,
                               te = b.cov$V1, stringsAsFactors = F))

  
b.relations = unique(b.relations)


b.relations

Define clusters

b.nodes = rbind(b.relations,
                    data.frame(sub.te = b.relations$te,
                               te = b.relations$sub.te))

b.nodes$comb = paste(b.nodes$sub.te, b.nodes$te, sep = '^')

comb.tbl = table(b.nodes$comb)
comb.back.and.foth = names(comb.tbl)[comb.tbl >= 2]
b.nodes = b.nodes[b.nodes$comb %in% comb.back.and.foth,]
b.nodes = unique(b.nodes[, c('sub.te', 'te')])


te.nodes <- igraph::make_graph(t(b.nodes), directed = T)
te.nodes <- igraph::simplify(te.nodes)
te.nodes.comp <- igraph::components(te.nodes)

nodes = paste('N', te.nodes.comp$membership, sep = '')
names(nodes) = names(te.nodes.comp$membership)

Identify family for each node


nodes.family = sapply(names(nodes), function(s) strsplit(s, '\\|')[[1]][6])

nodes.family.max = tapply(nodes.family, nodes, function(s){
  tbl = table(s)
  f = names(tbl)[tbl == max(tbl)]
  if(length(f) == 1){
    return(f)
  } else {
    return('Mix')
  }
})

nodes.family.max[nodes.family.max %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
nodes.family.max[nodes.family.max %in% c('RathE1_cons', 'RathE2_cons')] = 'DNA'
nodes.family.max[nodes.family.max %in% c('LINE/L1', 'LINE?')] = 'LINE'
nodes.family.max[nodes.family.max %in% c('Unassigned')] = 'Mix'
nodes.family.unique = unique(nodes.family.max)

Graph without singletons


b.graph.init = b.relations[(b.relations$sub.te %in% names(nodes)) & (b.relations$te %in% names(nodes)),]
b.graph = b.graph.init
b.graph = cbind(nodes[as.character(b.graph$sub.te)], nodes[as.character(b.graph$te)])
b.graph = unique(b.graph)


b.graph = b.graph[b.graph[,1] != b.graph[,2],]

# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]


# te.graph <- igraph::make_graph(t(b.graph), directed = T)
# te.graph <- igraph::simplify(te.graph)
# te.graph.comp <- igraph::components(te.graph)


nodes.family.max.graph = nodes.family.max[names(nodes.family.max) %in% unique(c(b.graph[,1], b.graph[,2]))]

graph.cols = sunset(length(unique(nodes.family.max.graph)))

graph.cols = discrete_rainbow(length(unique(nodes.family.max.graph)))
names(graph.cols) = unique(nodes.family.max.graph)
g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
p <- ggnet2(g.part, label = FALSE, edge.color = "black", node.size = 1, 
            color = nodes.family.max.graph, palette = graph.cols,
            mode = "kamadakawai")# + guides(size = FALSE)
p

Graph WITH singletons



names.core = names(nodes.family.max.graph)

b.graph.init = b.relations
for(i in 1:2){
  b.graph.init[b.graph.init[,i] %in% names(nodes), i] = nodes[b.graph.init[b.graph.init[,i] %in% names(nodes), i]]
}

b.graph = unique(b.graph.init)
b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph = unique(b.graph)
# Verteces from the previous graph
b.graph = b.graph[(b.graph[,1] %in% names.core) | (b.graph[,2] %in% names.core),]


# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]

te.graph <- igraph::make_graph(t(b.graph), directed = T)
d <- igraph::distances(te.graph)
# te.graph <- igraph::simplify(te.graph)
# te.graph.comp <- igraph::components(te.graph)

names.new = unique(setdiff(c(b.graph[,1], b.graph[,2]), names(nodes.family.max)))
# names.new.val = paste('G',1:length(names.new), sep = '')
# names(names.new.val) = names.new
# names.new.val = 

names.new.family = sapply(names.new, function(s) strsplit(s, '\\|')[[1]][6])
names.new.family[names.new.family %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
names.new.family[names.new.family %in% c('RathE1_cons', 'RathE2_cons')] = 'DNA'
names.new.family[names.new.family %in% c('LINE/L1', 'LINE?')] = 'LINE'
names.new.family[names.new.family %in% c('Unassigned')] = 'Mix'


nodes.family.max.add = c(nodes.family.max, names.new.family)
nodes.family.max.add = nodes.family.max.add[unique(c(b.graph[,1], b.graph[,2]))]

graph.cols = discrete_rainbow(length(unique(nodes.family.max.add)))
graph.cols = sample(graph.cols)
names(graph.cols) = unique(nodes.family.max.add)

g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
p <- ggnet2(g.part, label = FALSE, edge.color = "black", node.size = 0.5, 
            color = nodes.family.max.add,
            palette = graph.cols, mode = "kamadakawai")
p

TSNE



library(Rtsne)




d <- igraph::distances(te.graph)
d.max = max(d[!is.infinite(d)])

d[is.infinite(d)] = d.max * 1.3

tSNE <- Rtsne(d, is_distance = TRUE, dims = 2)

plot(tSNE$Y[,1], tSNE$Y[,2])
LS0tCnRpdGxlOiAiR3JhcGggb2YgVEVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKIyBTZXR1cApgYGB7ciwgbWVzc2FnZT1GQUxTRX0KIyBsaWJyYXJ5KGdncGxvdDIpCiMgbGlicmFyeShyZXNoYXBlMikKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KGNvbG9yUmFtcHMpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoJ2lncmFwaCcpCmxpYnJhcnkoZ2duZXQpCmxpYnJhcnkobmV0d29yaykKbGlicmFyeShraHJvbWEpCmxpYnJhcnkoZHBseXIpCgpzb3VyY2UoJ3NpbWlsYXJpdHkuUicpCgpzdW5zZXQgPC0gY29sb3VyKCJzdW5zZXQiKQpkaXNjcmV0ZV9yYWluYm93IDwtIGNvbG91cigiZGlzY3JldGUgcmFpbmJvdyIpCgpwYXRoLmJhc2UgPSAnLi4vLi4vLi4vJwpwYXRoLndvcmsgPSBwYXN0ZShwYXRoLmJhc2UsICcwMl9hbmFseXNpcy8wNF9zdi8wMV9kYXRhLycsIHNlcCA9ICcnKQpwYXRoLmZpZ3VyZXMgPSBwYXN0ZShwYXRoLmJhc2UsICcwMl9hbmFseXNpcy8wNF9zdi8wM19maWd1cmVzLycsIHNlcCA9ICcnKQpwYXRoLnN2cyA9IHBhc3RlKHBhdGguYmFzZSwgJzAxX2RhdGFfY29tbW9uLzAyX2Fubm90X2Rlbm92by8wMl9wYW5uYWdyYW0vc3ZzLycsIHNlcCA9ICcnKQojIHBhdGguZ2VuZXMgPSBwYXN0ZShwYXRoLmJhc2UsICcwMV9kYXRhX2NvbW1vbi8wMl9hbm5vdF9kZW5vdm8vMDJfcGFubmFncmFtL2dlbmVzLycsIHNlcCA9ICcnKQoKIyBzaW0uY3V0b2ZmID0gMC45CgpzaW0uY3V0b2ZmID0gMC44NQoKYGBgCgoKCiMgVEVzCmBgYHtyfQoKIyBMb2FkIHNpbWlsYXJpdHkgZnVuY3Rpb24KCmJsLmZpbGUgPSBwYXN0ZShwYXRoLndvcmssJ25ld190ZV9vbl90ZS5mYXN0YScsc2VwID0gJycpCmJsLnJlcyA9IHJlYWQudGFibGUoYmwuZmlsZSkKYmwucmVzID0gYmwucmVzW2JsLnJlcyRWMSAhPSBibC5yZXMkVjgsXQoKYmwucmVzLmluaXQgPSBibC5yZXMKYmwucmVzID0gYmwucmVzW2JsLnJlcyRWNiA+PSBzaW0uY3V0b2ZmICogMTAwLF0KCnJlcy5uZXN0ID0gZmluZE5lc3RlZG5lc3MoYmwucmVzLCB1c2Uuc3RyYW5kID0gRikKCnJlcy5uZXN0LmxlbiA9IHNhcHBseSh1bmlxdWUoYyhyZXMubmVzdCRWMSwgcmVzLm5lc3QkVjgpKSwgZnVuY3Rpb24ocykgYXMubnVtZXJpYyhzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs1XSkpCiAgCnJlcy5uZXN0JGxlbjEgPSByZXMubmVzdC5sZW5bcmVzLm5lc3QkVjFdCnJlcy5uZXN0JGxlbjggPSByZXMubmVzdC5sZW5bcmVzLm5lc3QkVjhdCnJlcy5uZXN0JHAxID0gcmVzLm5lc3QkQzEgLyByZXMubmVzdCRsZW4xCnJlcy5uZXN0JHA4ID0gcmVzLm5lc3QkQzggLyByZXMubmVzdCRsZW44CgpyZXMubmVzdC5zaW0gPSByZXMubmVzdFsocmVzLm5lc3QkcDEgPj0gc2ltLmN1dG9mZikgfCAKICAgICAgICAgICAgICAgICAgICAgICAgICAocmVzLm5lc3QkcDggPj0gc2ltLmN1dG9mZiksXQpgYGAKCiMjIEhvdyBtYW55IFRFcyBhcmUgaW4gdGhlIGdyYXBoCkRpc3RyaWJ1dGlvbiBhbW9uZyBmYW1pbGllcyBhbmQgc3ViZmFtaWxpZXMKRGlzdHJpYnV0aW9uIGFtb25nIGxlbmd0aHMKYGBge3J9CnRlLmluLmdyYXBoID0gdW5pcXVlKGMocmVzLm5lc3QkVjEsIHJlcy5uZXN0JFY4KSkKCiMgV2hhdCBpcyB0aGUgYWN0dWFsIG51bWJlciBvZiBURXMKZmlsZS5jb250ZW50IDwtIHJlYWRMaW5lcyhibC5maWxlKQoKc2VsZWN0ZWQubGluZXMgPC0gZmlsZS5jb250ZW50W2dyZXBsKCJeIyBRdWVyeTp8aGl0cyBmb3VuZCIsIGZpbGUuY29udGVudCldCmRmLnF1ZXJ5ID0gZGF0YS5mcmFtZShiLnF1ZXJ5PXNlbGVjdGVkLmxpbmVzW3NlcSgxLCBsZW5ndGgoc2VsZWN0ZWQubGluZXMpLCBieSA9IDIpXSwKICAgICAgICAgICAgICAgICAgICAgIGIuaGl0cz1zZWxlY3RlZC5saW5lc1tzZXEoMiwgbGVuZ3RoKHNlbGVjdGVkLmxpbmVzKSwgYnkgPSAyKV0pCgpkZi5xdWVyeSRxdWVyeSAgPC0gZ3N1YigiXiMgUXVlcnk6ICguKikiLCAiXFwxIiwgZGYucXVlcnkkYi5xdWVyeSkKZGYucXVlcnkkbGVuIDwtIGFzLm51bWVyaWMoc2FwcGx5KHN0cnNwbGl0KGRmLnF1ZXJ5JHF1ZXJ5LCAiXFx8IiksIGZ1bmN0aW9uKHgpIHhbNV0pKQpkZi5xdWVyeSRoaXRzIDwtIGFzLm51bWVyaWMoc3RyaW5ncjo6c3RyX2V4dHJhY3QoZGYucXVlcnkkYi5oaXRzLCAiXFxkKyIpKQpkZi5xdWVyeSR2YWwuaGl0cyA9IGRmLnF1ZXJ5JGhpdHMKZGYucXVlcnkkdmFsLmhpdHNbZGYucXVlcnkkdmFsLmhpdHMgPj0gMl0gPSAyCmRmLnF1ZXJ5JHZhbC5oaXRzW2RmLnF1ZXJ5JHF1ZXJ5ICVpbiUgYmwucmVzJFY4XSA9IDIKZGYucXVlcnkkdmFsLmhpdHNbZGYucXVlcnkkcXVlcnkgJWluJSB0ZS5pbi5ncmFwaF0gPSAzCmhpdC52YWx1ZXMgPSBjKCcwIGhpdHMnLCAnMSBzZWxmLWhpdCcsICdwYXJ0aWFsIG92ZXJsYXAnLCAnaW4gZ3JhcGgnLCAiaW4gZ3JhcGggYnV0IG5vdCBpbiBTVnMiKQpkZi5xdWVyeSRzLmhpdHMgPSBoaXQudmFsdWVzW2RmLnF1ZXJ5JHZhbC5oaXRzKzFdCmRmLnF1ZXJ5JHMuaGl0cyA9IGZhY3RvcihkZi5xdWVyeSRzLmhpdHMsIGxldmVscyA9IHJldihoaXQudmFsdWVzKSkKZGYucXVlcnkkZmFtaWx5IDwtIHNhcHBseShzdHJzcGxpdChkZi5xdWVyeSRxdWVyeSwgIlxcfCIpLCBmdW5jdGlvbih4KSB4WzldKQpkZi5xdWVyeSRzdWJmYW0gPC0gc2FwcGx5KHN0cnNwbGl0KGRmLnF1ZXJ5JHF1ZXJ5LCAiXFx8IiksIGZ1bmN0aW9uKHgpIHhbOF0pCgoKbXlfY29sb3JzIDwtIGNvbG9ycyA8LSBjKCJpbiBncmFwaCIgPSAiIzY3NkZBMyIsCiAgICAgICAgICAgICJwYXJ0aWFsIG92ZXJsYXAiID0gIiNGRjlGMjkiLAogICAgICAgICAgICAiMSBzZWxmLWhpdCIgPSAiIzZFQkY4QiIsCiAgICAgICAgICAgICIwIGhpdHMiID0gIiNEODIxNDgiLAogICAgICAgICAgICAiaW4gZ3JhcGggYnV0IG5vdCBpbiBTVnMiID0gIiMxNTFEM0IiKQoKCiMgVEVzLCB3aGljaCBhcmUgbm90IGluIFNWcwp0ZS5pbi5zdnMgPSByZWFkLnRhYmxlKHBhc3RlKHBhdGgud29yaywgJ2JsYXN0X3Rlc19vbl9zdi50eHQnLCBzZXAgPSAnJyksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp0ZS5yZXN0ID0gc2V0ZGlmZihkZi5xdWVyeSRxdWVyeSwgdGUuaW4uc3ZzJFYxKQp0ZS5pbi5zdnMgPSByZWFkLnRhYmxlKHBhc3RlKHBhdGgud29yaywgJ2JsYXN0X3N2X29uX3Rlcy50eHQnLCBzZXAgPSAnJyksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp0ZS5yZXN0ID0gc2V0ZGlmZih0ZS5yZXN0LCB0ZS5pbi5zdnMkVjgpCmRmLnF1ZXJ5JHMuaGl0c1tkZi5xdWVyeSRxdWVyeSAlaW4lIHRlLnJlc3RdID0gImluIGdyYXBoIGJ1dCBub3QgaW4gU1ZzIgoKCnAgPSBnZ3Bsb3QoZGYucXVlcnksIGFlcyh4ID0gbGVuLCBmaWxsID0gcy5oaXRzLCBjb2xvciA9IHMuaGl0cykpICsKICAjIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBhbHBoYT0wLjUsIGNvbG9yID0gImJsYWNrIiwgYmlucyA9IDMwKSArCiAgIyBnZW9tX2ppdHRlcihoZWlnaHQgPSAwLjAyLCB3aWR0aCA9IDAsIGFscGhhID0gMC43KSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC41KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbXlfY29sb3JzKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IG15X2NvbG9ycykgKwogICBzY2FsZV94X2xvZzEwKCkgKwogIGxhYnMoZmlsbCA9IE5VTEwsIGNvbG9yID0gTlVMTCkgKwogIHhsYWIoJ2xlbmd0aCBvZiBURXMnKSArIHlsYWIoJ05vcm1hbGlzZWQgZGVuc2l0eScpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMSwgMSksIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygxLCAxKSwKICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yID0gImdyZXk5MCIpKQoKcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3Rlc19zZWxmX2JsYXN0X2xlbl9kZW5zaXR5LnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgp0YWJsZShkZi5xdWVyeSR2YWwuaGl0cykKYGBgCgoKIyMjIFRFcyBub3QgaW4gU1ZzCmBgYHtyfQojIFRFcyBpbiB0ZS1ncmFwaDogdGUuaW4uZ3JhcGgKIyBURXMgd2hpY2ggYXJlIGhhdmUgbm8gY29ubmVjdGlvbiB0byBTVnMKCmRmID0gYXMuZGF0YS5mcmFtZSh0YWJsZShkZi5xdWVyeSRzLmhpdHMpKQoKICAKY29sb3JzIDwtIGMoImluIGdyYXBoIiA9ICIjNjc2RkEzIiwKICAgICAgICAgICAgInBhcnRpYWwgb3ZlcmxhcCIgPSAiI0ZGOUYyOSIsCiAgICAgICAgICAgICIxIHNlbGYtaGl0IiA9ICIjNkVCRjhCIiwKICAgICAgICAgICAgIjAgaGl0cyIgPSAiI0Q4MjE0OCIsCiAgICAgICAgICAgICJpbiBncmFwaCBidXQgbm90IGluIFNWcyIgPSAiIzE1MUQzQiIpCgoKcCA9IGdncGxvdChkZiwgYWVzKHggPSAiIiwgeSA9IEZyZXEsIGZpbGwgPSBWYXIxKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MSwgYWxwaGEgPSAwLjcpICsKICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApICsKICBsYWJzKHRpdGxlPU5VTEwsIGZpbGw9IkNhdGVnb3JpZXMiKSArCiAgdGhlbWVfdm9pZCgpKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JzKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEseCA9IDEuMyksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCnAKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAndGVzX3NlbGZfYmxhc3RfcGllX2NoYXJ0LnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSAzLCBoZWlnaHQgPSAzKQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgoKCgpgYGAKCiMjIEV4YW1wbGVzCiMjIyBFeGFtcGxlcyBubyBoaXRzCmBgYHtyfQoKCmRmLnF1ZXJ5LnRtcCA9IGRmLnF1ZXJ5WyhkZi5xdWVyeSR2YWwuaGl0cyA9PSAnMCcpLF0KCmNudC5pbml0ID0gYyh0YWJsZShkZi5xdWVyeSRmYW1pbHkpKQpjbnQudG1wID0gYyh0YWJsZShkZi5xdWVyeS50bXAkZmFtaWx5KSkKCmNvbW1vbl9uYW1lcyA8LSBpbnRlcnNlY3QobmFtZXMoY250LmluaXQpLCBuYW1lcyhjbnQudG1wKSkKIyDQodC+0LfQtNCw0L3QuNC1IGRhdGFmcmFtZSDRgtC+0LvRjNC60L4g0LTQu9GPINGB0L7QstC/0LDQtNCw0Y7RidC40YUg0LjQvNC10L0KZGZfbWF0Y2ggPC0gZGF0YS5mcmFtZShuYW1lcyA9IGNvbW1vbl9uYW1lcywgdmFsdWVzLmluaXQgPSBjbnQuaW5pdFtjb21tb25fbmFtZXNdLCAKICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMudG1wID0gY250LnRtcFtjb21tb25fbmFtZXNdKQoKCmdyYWRpZW50X2NvbG9ycyA8LSBjKGRpc2NyZXRlX3JhaW5ib3cobnJvdyhkZl9tYXRjaCkpKQpuYW1lcyhncmFkaWVudF9jb2xvcnMpID0gTlVMTAoKCnAgPSBnZ3Bsb3QoZGZfbWF0Y2gsIGFlcyh4ID0gdmFsdWVzLmluaXQsIHkgPSB2YWx1ZXMudG1wLCBsYWJlbCA9IG5hbWVzLCBjb2xvciA9IG5hbWVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgIyBnZW9tX3RleHQoaGp1c3QgPSAwLCB2anVzdCA9IDApICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwobWF4Lm92ZXJsYXBzID0gMjApICsKICB4bGFiKCJJbml0aWFsIGNvdW50cyIpICsKICB5bGFiKCJjb3VudHMgb2YgTm8gaGl0cyIpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGdyYWRpZW50X2NvbG9ycykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSArCiAgdGhlbWVfbWluaW1hbCgpCnAKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAndGVzX3NlbGZfc2NhdHRlcl9ub19oaXRzLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCiMgTm8gaGl0cyBhbmQgbG9uZwpsZW4ubWluID0gMTAwCmRmLnF1ZXJ5LnRtcCA9IGRmLnF1ZXJ5WyhkZi5xdWVyeSR2YWwuaGl0cyA9PSAnMCcpICYgKGRmLnF1ZXJ5JGxlbiA+PSBsZW4ubWluKSxdCgoKY250LmluaXQgPSBjKHRhYmxlKGRmLnF1ZXJ5JGZhbWlseSkpCmNudC50bXAgPSBjKHRhYmxlKGRmLnF1ZXJ5LnRtcCRmYW1pbHkpKQoKY29tbW9uX25hbWVzIDwtIGludGVyc2VjdChuYW1lcyhjbnQuaW5pdCksIG5hbWVzKGNudC50bXApKQojINCh0L7Qt9C00LDQvdC40LUgZGF0YWZyYW1lINGC0L7Qu9GM0LrQviDQtNC70Y8g0YHQvtCy0L/QsNC00LDRjtGJ0LjRhSDQuNC80LXQvQpkZl9tYXRjaCA8LSBkYXRhLmZyYW1lKG5hbWVzID0gY29tbW9uX25hbWVzLCB2YWx1ZXMuaW5pdCA9IGNudC5pbml0W2NvbW1vbl9uYW1lc10sIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcy50bXAgPSBjbnQudG1wW2NvbW1vbl9uYW1lc10pCgoKZ3JhZGllbnRfY29sb3JzIDwtIGMoZGlzY3JldGVfcmFpbmJvdyhucm93KGRmX21hdGNoKSkpCm5hbWVzKGdyYWRpZW50X2NvbG9ycykgPSBOVUxMCgpwID0gZ2dwbG90KGRmX21hdGNoLCBhZXMoeCA9IHZhbHVlcy5pbml0LCB5ID0gdmFsdWVzLnRtcCwgbGFiZWwgPSBuYW1lcywgY29sb3IgPSBuYW1lcykpICsKICBnZW9tX3BvaW50KCkgKwogICMgZ2VvbV90ZXh0KGhqdXN0ID0gMCwgdmp1c3QgPSAwKSArCiAgIyBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwobWF4Lm92ZXJsYXBzID0gMjApICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gcGFzdGUobmFtZXMsJygnLHZhbHVlcy50bXAsJyknLHNlcCA9JycpKSwgbWF4Lm92ZXJsYXBzID0gMjApICsKICB4bGFiKCJJbml0aWFsIGNvdW50cyIpICsKICB5bGFiKCJjb3VudHMgb2YgTm8gaGl0cyIpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGdyYWRpZW50X2NvbG9ycykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBnZW9tX3RleHQoYWVzKHg9MCx5PUluZixoanVzdD0wLCB2anVzdD0zLAogICAgICAgICAgICAgICAgbGFiZWw9cGFzdGUoJ0xlbmd0aCA+PScsIGxlbi5taW4pKSwgY29sb3IgPSAnZ3JleTIwJykKcAoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICd0ZXNfc2VsZl9zY2F0dGVyX25vX2hpdHNfbG9uZy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKYGBgCgpgYGB7cn0KCmhlYWQoZGYucXVlcnkudG1wW2RmLnF1ZXJ5LnRtcCRmYW1pbHkgPT0gJ0ROQS9NdURSJyxdJHF1ZXJ5KQpgYGAKCgojIyMgRXhhbXBsZXMgb25lIHNlbGYtaGl0CiMjIyMgZmFtaWxpZXMKYGBge3J9CgoKZGYucXVlcnkudG1wID0gZGYucXVlcnlbKGRmLnF1ZXJ5JHZhbC5oaXRzID09IDEpLF0KCmNudC5pbml0ID0gYyh0YWJsZShkZi5xdWVyeSRmYW1pbHkpKQpjbnQudG1wID0gYyh0YWJsZShkZi5xdWVyeS50bXAkZmFtaWx5KSkKCmNvbW1vbl9uYW1lcyA8LSBpbnRlcnNlY3QobmFtZXMoY250LmluaXQpLCBuYW1lcyhjbnQudG1wKSkKIyDQodC+0LfQtNCw0L3QuNC1IGRhdGFmcmFtZSDRgtC+0LvRjNC60L4g0LTQu9GPINGB0L7QstC/0LDQtNCw0Y7RidC40YUg0LjQvNC10L0KZGZfbWF0Y2ggPC0gZGF0YS5mcmFtZShuYW1lcyA9IGNvbW1vbl9uYW1lcywgdmFsdWVzLmluaXQgPSBjbnQuaW5pdFtjb21tb25fbmFtZXNdLCAKICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMudG1wID0gY250LnRtcFtjb21tb25fbmFtZXNdKQoKCmdyYWRpZW50X2NvbG9ycyA8LSBjKGRpc2NyZXRlX3JhaW5ib3cobnJvdyhkZl9tYXRjaCkpKQpuYW1lcyhncmFkaWVudF9jb2xvcnMpID0gTlVMTAoKCnAgPSBnZ3Bsb3QoZGZfbWF0Y2gsIGFlcyh4ID0gdmFsdWVzLmluaXQsIHkgPSB2YWx1ZXMudG1wLCBsYWJlbCA9IG5hbWVzLCBjb2xvciA9IG5hbWVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgIyBnZW9tX3RleHQoaGp1c3QgPSAwLCB2anVzdCA9IDApICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwobWF4Lm92ZXJsYXBzID0gMjApICsKICB4bGFiKCJJbml0aWFsIGNvdW50cyIpICsKICB5bGFiKCJDb3VudHMgaW4gXCIxIHNlbGYtaGl0c1wiIGNhdGVnb3J5IikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZ3JhZGllbnRfY29sb3JzKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKICB0aGVtZV9taW5pbWFsKCkKcAoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICd0ZXNfc2VsZl9zY2F0dGVyXzFfc2VsZmhpdHNfZmFtLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKYGBgCgojIyMjIHN1YmZhbWlsaWVzCmBgYHtyfQoKCmRmLnF1ZXJ5LnRtcCA9IGRmLnF1ZXJ5WyhkZi5xdWVyeSR2YWwuaGl0cyA9PSAxKSAmIChkZi5xdWVyeSRsZW4gPj0gNjAwKSxdCgpjbnQuaW5pdCA9IGModGFibGUoZGYucXVlcnkkc3ViZmFtKSkKY250LnRtcCA9IGModGFibGUoZGYucXVlcnkudG1wJHN1YmZhbSkpCgpjb21tb25fbmFtZXMgPC0gaW50ZXJzZWN0KG5hbWVzKGNudC5pbml0KSwgbmFtZXMoY250LnRtcCkpCiMg0KHQvtC30LTQsNC90LjQtSBkYXRhZnJhbWUg0YLQvtC70YzQutC+INC00LvRjyDRgdC+0LLQv9Cw0LTQsNGO0YnQuNGFINC40LzQtdC9CmRmX21hdGNoIDwtIGRhdGEuZnJhbWUobmFtZXMgPSBjb21tb25fbmFtZXMsIHZhbHVlcy5pbml0ID0gY250LmluaXRbY29tbW9uX25hbWVzXSwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzLnRtcCA9IGNudC50bXBbY29tbW9uX25hbWVzXSkKCgojIGdyYWRpZW50X2NvbG9ycyA8LSBjKGRpc2NyZXRlX3JhaW5ib3cobnJvdyhkZl9tYXRjaCkpKQpuYW1lcyhncmFkaWVudF9jb2xvcnMpID0gTlVMTAoKCnAgPSBnZ3Bsb3QoZGZfbWF0Y2gsIGFlcyh4ID0gdmFsdWVzLmluaXQsIHkgPSB2YWx1ZXMudG1wLCBsYWJlbCA9IG5hbWVzLCBjb2xvciA9IG5hbWVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgIyBnZW9tX3RleHQoaGp1c3QgPSAwLCB2anVzdCA9IDApICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwobWF4Lm92ZXJsYXBzID0gMjApICsKICB4bGFiKCJJbml0aWFsIGNvdW50cyIpICsKICB5bGFiKCJDb3VudHMgaW4gXCIxIHNlbGYtaGl0c1wiIGNhdGVnb3J5IikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICAjIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBncmFkaWVudF9jb2xvcnMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogIHRoZW1lX21pbmltYWwoKQpwCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3Rlc19zZWxmX3NjYXR0ZXJfMV9zZWxmaGl0c19zdWJmYW0ucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCmBgYAoKIyMjIyBpbmRpdmlkdWFscyBmcm9tIHN1YmZhbWlsaWVzCmBgYHtyfQpzLnN1YmZhbSA9ICdBVFJFUDgnCgpkZi5xdWVyeS50bXAgPSBkZi5xdWVyeVsoZGYucXVlcnkkc3ViZmFtID09IHMuc3ViZmFtKSAmIChkZi5xdWVyeSRsZW4gPj0gNjAwKSxdCmRmLnF1ZXJ5LnRtcAoKCmBgYAoKCgoKCiMjIENyZWF0aW5nIHRoZSBncmFwaApgYGB7cn0KIyBhbGwgZWRnZXMKaWR4ID0gcmVzLm5lc3QkcDEgPj0gc2ltLmN1dG9mZgplZGdlcyA9IGNiaW5kKHJlcy5uZXN0JFYxW2lkeF0sIHJlcy5uZXN0JFY4W2lkeF0pCmlkeCA9IHJlcy5uZXN0JHA4ID49IHNpbS5jdXRvZmYKZWRnZXMgPSByYmluZChlZGdlcywgY2JpbmQocmVzLm5lc3QkVjhbaWR4XSwgcmVzLm5lc3QkVjFbaWR4XSkpCnRlLmVuZ2VzLm5hbWVzID0gdW5pcXVlKGMoZWRnZXNbLDFdLCBlZGdlc1ssMl0pKQp0ZS5lbmdlcy5mYW0gPSBzYXBwbHkodGUuZW5nZXMubmFtZXMsIGZ1bmN0aW9uKHMpIHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzldICkKCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdETkEvUG9nbycsICdETkEvVGMxJywgJ0ROQS9IYXJiaW5nZXInLCAnRE5BL0VuLVNwbScsCiAgICAgICAgICAgICAgICAgICAgICdETkEvSEFUJywgJ0ROQScsICdETkEvTWFyaW5lcicpXSA9ICdETkEnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdSYXRoRTFfY29ucycsICdSYXRoRTJfY29ucycsICdSYXRoRTNfY29ucycpXSA9ICdSYXRoRTEvMi8zX2NvbnMnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdMSU5FL0wxJywgJ0xJTkU/JyldID0gJ0xJTkUnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdVbmFzc2lnbmVkJyldID0gJ01peCcKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ1JDL0hlbGl0cm9uJyldID0gJ0hlbGl0cm9uJwoKZWRnZXMgPSBlZGdlc1t0ZS5lbmdlcy5mYW1bZWRnZXNbLDFdXSAhPSAnVEVHJyxdCmVkZ2VzID0gZWRnZXNbdGUuZW5nZXMuZmFtW2VkZ2VzWywyXV0gIT0gJ1RFRycsXQp0ZS5lbmdlcy5uYW1lcyA9IHVuaXF1ZShjKGVkZ2VzWywxXSwgZWRnZXNbLDJdKSkKCgojIG5vZGVzCmlkeCA9IChyZXMubmVzdCRwMSA+PSBzaW0uY3V0b2ZmKSAmIChyZXMubmVzdCRwOCA+PSBzaW0uY3V0b2ZmKQp0ZS5ub2RlcyA9IGNiaW5kKHJlcy5uZXN0JFYxW2lkeF0sIHJlcy5uZXN0JFY4W2lkeF0pCnRlLm5vZGVzID0gdGUubm9kZXNbdGUuZW5nZXMuZmFtW3RlLm5vZGVzWywxXV0gIT0gJ1RFRycsXQp0ZS5ub2RlcyA9IHRlLm5vZGVzW3RlLmVuZ2VzLmZhbVt0ZS5ub2Rlc1ssMl1dICE9ICdURUcnLF0KCnRlLnJlc3QgPSBzZXRkaWZmKHRlLmVuZ2VzLm5hbWVzLCBjKHRlLm5vZGVzWywxXSwgdGUubm9kZXNbLDJdKSkKCgp0ZS5ub2Rlcy5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodCh0ZS5ub2RlcyksIGRpcmVjdGVkID0gVCkKdGUubm9kZXMuZ3JhcGggPC0gaWdyYXBoOjpzaW1wbGlmeSh0ZS5ub2Rlcy5ncmFwaCkKdGUubm9kZXMuY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHModGUubm9kZXMuZ3JhcGgpCgpub2RlcyA9IGRhdGEuZnJhbWUobm9kZSA9IHBhc3RlKCdOJywgdGUubm9kZXMuY29tcCRtZW1iZXJzaGlwLCBzZXAgPSAnJyksIAogICAgICAgICAgICAgICAgICAgdGUgPSBuYW1lcyh0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXApKQoKbm9kZXMucmVzdCA9IGRhdGEuZnJhbWUobm9kZSA9IHBhc3RlKCdSJywgKDE6bGVuZ3RoKHRlLnJlc3QpKSwgc2VwID0gJycpLCB0ZSA9IHRlLnJlc3QpCm5vZGVzID0gcmJpbmQobm9kZXMsIG5vZGVzLnJlc3QpCgpyb3duYW1lcyhub2RlcykgPSBub2RlcyR0ZQoKCm5vZGVzLmNudCA9IGRhdGEuZnJhbWUoY250ID0gYyh0YWJsZShub2RlcyRub2RlKSkpCm5vZGVzLmNudCRub2RlID0gcm93bmFtZXMobm9kZXMuY250KQpub2Rlcy5jbnQkZmFtID0gc2FwcGx5KG5vZGVzLmNudCRub2RlLCBmdW5jdGlvbihzKXsKICBzLnRlID0gbm9kZXMkdGVbbm9kZXMkbm9kZSA9PSBzXQogIGZhbS50ZSA9IHVuaXF1ZSh0ZS5lbmdlcy5mYW1bcy50ZV0pCiAgaWYobGVuZ3RoKGZhbS50ZSkgPT0gMSl7CiAgICByZXR1cm4oZmFtLnRlKQogIH0gZWxzZSB7CiAgICBmYW0udGUgPSBzZXRkaWZmKGZhbS50ZSwgJ1RFRycpCiAgICBpZihsZW5ndGgoZmFtLnRlKSA9PSAxKSByZXR1cm4oZmFtLnRlKQogICAgcmV0dXJuKCdNaXgnKQogIH0KfSkKdGFibGUobm9kZXMuY250JGZhbSkKCgojIFJlZGVmaW5lIGVkZ2VzIGJ1dCB3aXRoIG5vZGUgbmFtZXMKaWR4LmVuZGVzID0gKGVkZ2VzWywxXSAlaW4lIG5vZGVzJHRlKSAmIChlZGdlc1ssMl0gJWluJSBub2RlcyR0ZSkKYi5ncmFwaCA9IGNiaW5kKG5vZGVzW2VkZ2VzW2lkeC5lbmRlcywxXSwgJ25vZGUnXSxub2Rlc1tlZGdlc1tpZHguZW5kZXMsMl0sICdub2RlJ10pCmIuZ3JhcGggPSB1bmlxdWUoYi5ncmFwaCkKIyBiLmdyYXBoID0gYi5ncmFwaFtiLmdyYXBoWywxXSAhPSBiLmdyYXBoWywyXSxdCmIuZ3JhcGgudW5pID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoWywyXSxdCmIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KCmxlbmd0aCh1bmlxdWUoYyhiLmdyYXBoWywxXSwgYi5ncmFwaFssMl0pKSkKCiMgcmVkdWNlIGluZGlyZWN0IGFycm93cwppZHgucmVtb3ZlID0gYygpCmZvcihpLmVkZ2UgaW4gMTpucm93KGIuZ3JhcGgpKXsKICBpZihpLmVkZ2UgJSUgMTAwMCA9PSAwKSBwcmludChpLmVkZ2UpCiAgdG1wLnRvID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoW2kuZWRnZSwxXSwyXQogIHRtcC5mcm9tID0gYi5ncmFwaFtiLmdyYXBoWywyXSA9PSBiLmdyYXBoW2kuZWRnZSwyXSwxXQogIGlmKGxlbmd0aChpbnRlcnNlY3QodG1wLnRvLCB0bXAuZnJvbSkpID4gMCkgaWR4LnJlbW92ZSA9IGMoaWR4LnJlbW92ZSwgaS5lZGdlKQp9CmlkeC5yZW1vdmUgPSB1bmlxdWUoaWR4LnJlbW92ZSkKYi5ncmFwaCA9IGIuZ3JhcGhbLWlkeC5yZW1vdmUsXQojIGIuZ3JhcGggPSByYmluZChiLmdyYXBoLCBiLmdyYXBoLnVuaSkKCiMgUHJpbnQgZ3JhcGgKCmcubm9kZXMuZmFtID0gbm9kZXMuY250JGZhbQpuYW1lcyhnLm5vZGVzLmZhbSkgPSBub2Rlcy5jbnQkbm9kZQpnLm5vZGVzLmNudCA9IG5vZGVzLmNudCRjbnQKbmFtZXMoZy5ub2Rlcy5jbnQpID0gbm9kZXMuY250JG5vZGUKCmcuY29scyA9IGRpc2NyZXRlX3JhaW5ib3cobGVuZ3RoKHVuaXF1ZShnLm5vZGVzLmZhbSkpKQpuYW1lcyhnLmNvbHMpID0gdW5pcXVlKGcubm9kZXMuZmFtKQoKYi5ncmFwaC5pbml0ID0gYi5ncmFwaAoKCmcucGFydCA8LSBuZXR3b3JrKGIuZ3JhcGgsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0KQpgYGAKCiMjIyBPbGQgY29sb3JzCmBgYHtyfQojIHAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiMgICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAojICAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiMgICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scywKIyAgICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiMgICAgICAgICAgICAgKSAKIyBwICsgZ3VpZGVzKHNpemUgPSBGKQojIAojICMgCiMgIyBiLmdyYXBoLmZhbSA9IGNiaW5kKGcubm9kZXMuZmFtW2IuZ3JhcGhbLDFdXSwgZy5ub2Rlcy5mYW1bYi5ncmFwaFssMl1dKQojICMgYi5ncmFwaC5mYW0KIyAjIAojICMgd2hpY2goKGIuZ3JhcGguZmFtWywxXSA9PSAnRE5BL011RFInKSAmIChiLmdyYXBoLmZhbVssMV0gPT0gJ0xJTkUnKSkKIyAKCgpgYGAKCiMjIyBOZXcgRmFtaWx5IGNvbG9ycwpgYGB7cn0KZy5mYW0ubmFtZXMgPSBzb3J0KHVuaXF1ZShnLm5vZGVzLmZhbSkpCmZhbS5wYWxldHRlID0gYygpCmlkeC5wYWxsZXRlID0gYygpCgppZHguZmFtIDwtIGdyZXAoIl5IZWxpdHJvbiIsIGcuZmFtLm5hbWVzLCB2YWx1ZSA9IEZBTFNFKQp0bXAucGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGMoJyNCRkFDRTInLCAnIzI2NkQ5OCcsICcjNDIyQjcyJykpKGxlbmd0aChpZHguZmFtKSkKaWR4LnBhbGxldGUgPSBjKGlkeC5wYWxsZXRlLCBpZHguZmFtKQpmYW0ucGFsZXR0ZSA9IGMoZmFtLnBhbGV0dGUsIHRtcC5wYWxldHRlKQoKaWR4LmZhbSA8LSBncmVwKCJeTFRSIiwgZy5mYW0ubmFtZXMsIHZhbHVlID0gRkFMU0UpCnRtcC5wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygnI0JGREIzOCcsICcjNTRCNDM1JykpKGxlbmd0aChpZHguZmFtKSkKaWR4LnBhbGxldGUgPSBjKGlkeC5wYWxsZXRlLCBpZHguZmFtKQpmYW0ucGFsZXR0ZSA9IGMoZmFtLnBhbGV0dGUsIHRtcC5wYWxldHRlKQoKaWR4LmZhbSA8LSBncmVwKCJeRE5BIiwgZy5mYW0ubmFtZXMsIHZhbHVlID0gRkFMU0UpCnRtcC5wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygnI0Y5QjVEMCcsICcjOTcxNTQ5JykpKGxlbmd0aChpZHguZmFtKSkKaWR4LnBhbGxldGUgPSBjKGlkeC5wYWxsZXRlLCBpZHguZmFtKQpmYW0ucGFsZXR0ZSA9IGMoZmFtLnBhbGV0dGUsIHRtcC5wYWxldHRlKQoKaWR4LmZhbSA9IHNldGRpZmYoMTpsZW5ndGgoZy5mYW0ubmFtZXMpLCBpZHgucGFsbGV0ZSkKdG1wLnBhbGV0dGUgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCcjRkZDMjZGJywgJyNDMzgxNTQnLCAnIzg4NEEzOScsICcjNEUzNjM2JykpKGxlbmd0aChpZHguZmFtKSkKaWR4LnBhbGxldGUgPSBjKGlkeC5wYWxsZXRlLCBpZHguZmFtKQpmYW0ucGFsZXR0ZSA9IGMoZmFtLnBhbGV0dGUsIHRtcC5wYWxldHRlKQoKbmFtZXMoZmFtLnBhbGV0dGUpID0gZy5mYW0ubmFtZXNbaWR4LnBhbGxldGVdCmZhbS5wYWxldHRlWydVbmFzc2lnbmVkJ10gPSAnZ3JleScKZmFtLnBhbGV0dGVbJ01peCddID0gJ2JsYWNrJwpmYW0ucGFsZXR0ZVsnVEVHJ10gPSAnZGFya2dyZWVuJwoKCgoKYGBgCgpgYGB7cn0KCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSwKICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgogICAgICAgICAgICApIApwID0gcCArIGd1aWRlcyhzaXplID0gRikgCnAgPSBwICsgY29vcmRfZml4ZWQocmF0aW8gPSAxKQoKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfdGVzX2ZhbWlseS5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocCsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfdGVzX2ZhbWlseV9sZWdlbmQucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgoKCgojIyBTZXBhcmF0ZWx5IHZpc3VhbGlzZSBjb25uZWN0ZWQgY29tcG9uZW50cwpgYGB7cn0KdG1wLmdyYXBoIDwtIGlncmFwaDo6bWFrZV9ncmFwaCh0KGIuZ3JhcGgpLCBkaXJlY3RlZCA9IFQpCnRtcC5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRtcC5ncmFwaCkKdG1wLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRtcC5ncmFwaCkKCnRtcC5jbnQgPSB0YWJsZSh0bXAuY29tcCRtZW1iZXJzaGlwKQp0bXAuY250ID0gLXNvcnQoLXRtcC5jbnQpCmhlYWQodG1wLmNudCkKCmsgPSAxCnRtcC5rID0gYXMubnVtZXJpYyhuYW1lcyh0bXAuY250KVtrXSkKdG1wLm5hbWVzID0gbmFtZXModG1wLmNvbXAkbWVtYmVyc2hpcClbdG1wLmNvbXAkbWVtYmVyc2hpcCA9PSB0bXAua10KYi5ncmFwaC5zdWIgPSBiLmdyYXBoWyhiLmdyYXBoWywxXSAlaW4lIHRtcC5uYW1lcykgJiAKICAgICAgICAgICAgICAgICAgICAgICAgKGIuZ3JhcGhbLDJdICVpbiUgdG1wLm5hbWVzKSxdCgpnLnBhcnQuc3ViLmJpZyA8LSBuZXR3b3JrKGIuZ3JhcGguc3ViLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKYi5ncmFwaC5uYW1lcy5zdWIuYmlnID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0LnN1Yi5iaWcpCgoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydC5zdWIuYmlnLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwKICAgICAgICAgICAgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCnAuYmlnLnR5cGUgPSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyBzZXQuc2VlZCgyMCkKIyBwIDwtIGdnbmV0MihnLnBhcnQuc3ViLmJpZywgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiMgICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwgCiMgICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLAojICAgICAgICAgICAgIG1vZGUgPSAna2FtYWRha2F3YWknLAojICAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCiMgcC5iaWcuY29sb3IgPSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKCnRtcC5rID0gYXMubnVtZXJpYyhuYW1lcyh0bXAuY250KVtrXSkKdG1wLm5hbWVzID0gbmFtZXModG1wLmNvbXAkbWVtYmVyc2hpcClbdG1wLmNvbXAkbWVtYmVyc2hpcCAhPSB0bXAua10KYi5ncmFwaC5zdWIgPSBiLmdyYXBoWyhiLmdyYXBoWywxXSAlaW4lIHRtcC5uYW1lcykgJiAKICAgICAgICAgICAgICAgICAgICAgICAgKGIuZ3JhcGhbLDJdICVpbiUgdG1wLm5hbWVzKSxdCgpnLnBhcnQuc3ViLnNtYWxsIDwtIG5ldHdvcmsoYi5ncmFwaC5zdWIsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbCA9IG5ldHdvcmsudmVydGV4Lm5hbWVzKGcucGFydC5zdWIuc21hbGwpCgoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydC5zdWIuc21hbGwsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwKICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikKcC5zbWFsbC50eXBlID1wICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyBzZXQuc2VlZCgyMCkKIyBwIDwtIGdnbmV0MihnLnBhcnQuc3ViLnNtYWxsLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKIyAgICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sIAojICAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLAojICAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiMgICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikKIyBwLnNtYWxsLmNvbG9yID0gcCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCgoKYGBgCiMjIyBQbG90cwpgYGB7cn0KcC5iaWcudHlwZQpwLnNtYWxsLnR5cGUKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfdGVzX2ZhbWlseV9zbWFsbC5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKcHJpbnQocC5zbWFsbC50eXBlKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF90ZXNfZmFtaWx5X2JpZy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocC5iaWcudHlwZSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpgYGAKCiMgU3RvcCBmb3IgdGhlIHBhcGVyCmBgYHtyfQpzdG9wKCkKYGBgCgoKIyMgU3BlY2lmaWMgVEUgZmFtaWxpZXMKIyMjIEdyYXBoIG9mIG9uZSBmYW1pbHkKCmBgYHtyfQpzb3J0KC10YWJsZShkZi5xdWVyeSRzdWJmYW1bKGRmLnF1ZXJ5JHZhbC5oaXRzID09IDMpICYgKGRmLnF1ZXJ5JGZhbWlseSA9PSAnTFRSL0NvcGlhJyldKSkKYGBgCgoKCmBgYHtyfQoKIyBvbmUudGUuZmFtID0gJ0JST0RZQUdBMScKIyBvbmUudGUuZmFtID0gJ0JST0RZQUdBMicKIyBvbmUudGUuZmFtID0gJ0hFTElUUk9OWTFEJwojIG9uZS50ZS5mYW0gPSAnSEVMSVRST05ZMycKb25lLnRlLmZhbSA9ICdBVENPUElBNDEnCnF1ZXJ5LmZhbSA9IGRmLnF1ZXJ5JHF1ZXJ5W2RmLnF1ZXJ5JHN1YmZhbSA9PSBvbmUudGUuZmFtXQoKCm9uZS50ZS5mYW0gPSAnQVRDT1BJQTQxJwpxdWVyeS5mYW0gPSBkZi5xdWVyeSRxdWVyeVtkZi5xdWVyeSRzdWJmYW0gPT0gb25lLnRlLmZhbV0KCnJlcy5uZXN0LmZhbXAgPSByZXMubmVzdFsocmVzLm5lc3QkVjEgJWluJSBxdWVyeS5mYW0pIHwgKHJlcy5uZXN0JFY4ICVpbiUgcXVlcnkuZmFtKSxdCgoKaWR4ID0gcmVzLm5lc3QuZmFtcCRwMSA+PSBzaW0uY3V0b2ZmCmVkZ2VzID0gY2JpbmQocmVzLm5lc3QuZmFtcCRWMVtpZHhdLCByZXMubmVzdC5mYW1wJFY4W2lkeF0pCmlkeCA9IHJlcy5uZXN0LmZhbXAkcDggPj0gc2ltLmN1dG9mZgplZGdlcyA9IHJiaW5kKGVkZ2VzLCBjYmluZChyZXMubmVzdC5mYW1wJFY4W2lkeF0sIHJlcy5uZXN0LmZhbXAkVjFbaWR4XSkpCgoKdGUuZW5nZXMubmFtZXMgPSB1bmlxdWUoYyhlZGdlc1ssMV0sIGVkZ2VzWywyXSkpCnRlLmVuZ2VzLmZhbSA9IHNhcHBseSh0ZS5lbmdlcy5uYW1lcywgZnVuY3Rpb24ocykgc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bOV0gKQp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnRE5BL1BvZ28nLCAnRE5BL1RjMScsICdETkEvSGFyYmluZ2VyJywgJ0ROQS9Fbi1TcG0nLAogICAgICAgICAgICAgICAgICAgICAnRE5BL0hBVCcsICdETkEnLCAnRE5BL01hcmluZXInKV0gPSAnRE5BJwp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnUmF0aEUxX2NvbnMnLCAnUmF0aEUyX2NvbnMnLCAnUmF0aEUzX2NvbnMnKV0gPSAnUmF0aEUxLzIvM19jb25zJwp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnTElORS9MMScsICdMSU5FPycpXSA9ICdMSU5FJwp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnVW5hc3NpZ25lZCcpXSA9ICdNaXgnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdSQy9IZWxpdHJvbicpXSA9ICdIZWxpdHJvbicKCmcucGFydCA8LSBuZXR3b3JrKGVkZ2VzLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKYi5ncmFwaC5uYW1lcyA9IG5ldHdvcmsudmVydGV4Lm5hbWVzKGcucGFydCkKYi5ncmFwaC5sZW4gPSBhcy5udW1lcmljKHNhcHBseShzdHJzcGxpdChiLmdyYXBoLm5hbWVzLCAiXFx8IiksIGZ1bmN0aW9uKHgpIHhbNV0pKQoKCmxhYmVsLmZhbWlseSA9IHNhcHBseShzdHJzcGxpdChiLmdyYXBoLm5hbWVzLCAiXFx8IiksIGZ1bmN0aW9uKHgpIHhbOF0pCmxhYi5jb2xzID0gYygnIzNGMkUzRScsICJ3aGl0ZSIpCmxhYmVsLmNvbG9yID0gbGFiLmNvbHNbKGxhYmVsLmZhbWlseSA9PSBvbmUudGUuZmFtKSArIDFdCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IGIuZ3JhcGgubGVuLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICBub2RlLnNpemUgPSAxNSwKICAgICAgICAgICAgYWxwaGE9MC44LAogICAgICAgICAgICBhcnJvdy5nYXAgPSAwLjAxNSwKICAgICAgICAgICAgYXJyb3cuc2l6ZSA9IDUsCiAgICAgICAgICAgIGxhYmVsLmNvbG9yID0gbGFiZWwuY29sb3IsCiAgICAgICAgICAgICMgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBjb2xvciA9IHRlLmVuZ2VzLmZhbVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAncmVhbF90ZXNfc3ViZmFtXycsIG9uZS50ZS5mYW0sICcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAxOCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IGIuZ3JhcGgubmFtZXMsIGVkZ2UuY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgbm9kZS5zaXplID0gMTUsCiAgICAgICAgICAgIGFscGhhPTAuOCwKICAgICAgICAgICAgYXJyb3cuZ2FwID0gMC4wMTUsCiAgICAgICAgICAgIGFycm93LnNpemUgPSA1LAogICAgICAgICAgICAjIGxhYmVsLmNvbG9yID0gbGFiZWwuY29sb3IsCiAgICAgICAgICAgICMgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgIGNvbG9yID0gdGUuZW5nZXMuZmFtW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUsCiAgICAgICAgICAgICMgbW9kZSA9ICJrYW1hZGFrYXdhaSIKICAgICAgICAgICAgKSArIGd1aWRlcyhzaXplID0gRikKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdyZWFsX3Rlc19zdWJmYW1fJywgb25lLnRlLmZhbSwgJ19uYW1lcy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNTAsIGhlaWdodCA9IDQ5KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCmBgYAoKCiMjIERvdHBsb3RzCiMjIyMgRnVuY3Rpb25zCmBgYHtyfQoKCnNlcTJteCA8LSBmdW5jdGlvbihzZXEsIHdzaXplKXsKICAKICBudW1fcm93cyA8LSBsZW5ndGgoc2VxKSAtIHdzaXplICsgMQogIG1hdHJpeF9zZXEgPC0gbWF0cml4KG5yb3cgPSBudW1fcm93cywgbmNvbCA9IHdzaXplKQogIGZvciAoaSBpbiAxOm51bV9yb3dzKSB7CiAgICBtYXRyaXhfc2VxW2ksIF0gPC0gc2VxW2k6KGkgKyB3c2l6ZSAtIDEpXQogIH0KCiAgcmV0dXJuKG1hdHJpeF9zZXEpCn0KCm14Q29tcCA8LSBmdW5jdGlvbihteDEsIG14Miwgd3NpemUsIG5tYXRjaCl7CiAgbXgucmVzID0gMAogIGZvcihzIGluIGMoJ0EnLCAnQycsICdHJywgJ1QnKSl7CiAgICBteC5yZXMgPSBteC5yZXMgKyAobXgxID09IHMpICUqJSB0KG14MiA9PSBzKQogIH0KICAjIG14LnJlcyA9IChteC5yZXMgPj0gbm1hdGNoKSAqIDEKICBteC5yZXNbbXgucmVzIDwgbm1hdGNoXSA9IDAKICAKICBpbmRpY2VzIDwtIHdoaWNoKG14LnJlcyAhPSAwLCBhcnIuaW5kID0gVFJVRSkKICB2YWx1ZXMgPC0gbXgucmVzW2luZGljZXNdCiAgcmVzdWx0IDwtIGNiaW5kKGluZGljZXMsIHZhbHVlcykKICByZXN1bHQgPSBhcy5kYXRhLmZyYW1lKHJlc3VsdCkKICByZXR1cm4ocmVzdWx0KQp9Cgpkb3RwbG90IDwtIGZ1bmN0aW9uKHNlcTEsIHNlcTIsIHdzaXplLCBubWF0Y2gpIHsKICBzZXEyLnJjID0gcmV2KHNlcWlucjo6Y29tcChzZXEyKSkKCiAgbXgxID0gdG91cHBlcihzZXEybXgoc2VxMSwgd3NpemUpKQogIG14MiA9IHRvdXBwZXIoc2VxMm14KHNlcTIsIHdzaXplKSkKICAKICByZXN1bHQgPSBteENvbXAobXgxLCBteDIsIHdzaXplLCBubWF0Y2gpCiAgCiAgbXgyLnJjID0gdG91cHBlcihzZXEybXgoc2VxMi5yYywgd3NpemUpKQogIHJlc3VsdC5yYyA9IG14Q29tcChteDEsIG14Mi5yYywgd3NpemUsIG5tYXRjaCkKICByZXN1bHQucmMkdmFsdWVzID0gLXJlc3VsdC5yYyR2YWx1ZXMKICByZXN1bHQucmMkY29sID0gbGVuZ3RoKHNlcTIpIC0gcmVzdWx0LnJjJGNvbCAtIHdzaXplICsgMgogIHJlc3VsdCA9IHJiaW5kKHJlc3VsdC5yYywgcmVzdWx0KQogIAogIAogIHAgPSBnZ3Bsb3QocmVzdWx0LCBhZXMoeCA9IHJvdywgeSA9IGNvbCwgZmlsbCA9IHZhbHVlcykpICsKICAgIGdlb21fdGlsZSh3aWR0aCA9IDEsIGhlaWdodCA9IDEpICsKICAgICMgeGxhYihuYW1lMSkgKyB5bGFiKG5hbWUyKSArCiAgICAjIHhsYWIocGFzdGUwKHN0cnNwbGl0KG5hbWUxLCAnXFx8JylbWzFdXVs3OjldLCBjb2xsYXBzZSA9ICd8JykpICsgCiAgICAjIHlsYWIocGFzdGUwKHN0cnNwbGl0KG5hbWUyLCAnXFx8JylbWzFdXVs3OjldLCBjb2xsYXBzZSA9ICd8JykpICsKICAgICMgeGxhYignJykgKyB5bGFiKCcnKSArCiAgICBndWlkZXMoZmlsbCA9IEZBTFNFKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKyBjb29yZF9maXhlZCgpICsKICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKSArIAogICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICIjQ0UxRjZBIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICIjMjczNzREIiwgbWlkcG9pbnQgPSAwKSArCiAgICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTEpKQogIHAgCiAgcmV0dXJuKHApCn0KCmBgYAoKYGBge3J9CmZpbGUudGUuZmFzdGEgPSAnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi90YWlyL25ld19maWx0cmF0aW9uL25ld190ZS5mYXN0YScKdGUuZmFzdGEgPSBzZXFpbnI6OnJlYWQuZmFzdGEoZmlsZS50ZS5mYXN0YSkKdGUubmFtZXMgPSBuYW1lcyh0ZS5mYXN0YSkKdGUuZmFzdGEgPSBzZXFpbnI6OmdldFNlcXVlbmNlKHRlLmZhc3RhKQpuYW1lcyh0ZS5mYXN0YSkgPSB0ZS5uYW1lcwpgYGAKCiMjIyBPbmUgcGFpcndpc2UgZXhhbXBsZQpgYGB7cn0Kd3NpemUgPSAxMApubWF0Y2ggPSA4CgpzZXExID0gdGUuZmFzdGFbW2IuZ3JhcGgubmFtZXNbMV1dXQpzZXEyID0gdGUuZmFzdGFbW2IuZ3JhcGgubmFtZXNbMV1dXQoKCm5hbWUxID0gJ3RlfDEyMzg0NzYzfDEyMzg1MjYyfDR8NTAwfCt8QVQ0VEU1NzU4MHxCUk9EWUFHQTFBfEROQS9NdURSJwpuYW1lMiA9ICd0ZXwxMzY3NDkxN3wxMzY3NTI3MXwxfDM1NXwrfEFUMVRFNDQ3NjB8QlJPRFlBR0ExfEROQS9NdURSJwoKIyBuYW1lMSA9ICd0ZXwxMDU5MjExMXwxMDU5MjY2NHwxfDU1NHwtfEFUMVRFMzQyNjV8QlJPRFlBR0EyfEROQS9NdURSJwojIG5hbWUyID0gJ3RlfDg3NDMyMzh8ODc0NDI2M3w0fDEwMjZ8LXxBVDRURTM5MDQ1fEhFTElUUk9OWTFEfFJDL0hlbGl0cm9uJwoKbmFtZTEgPSAndGV8NjI4MzE5OHw2Mjg0NDIxfDR8MTIyNHwtfEFUNFRFMjY3MTB8QVRSRVAxNXxSQy9IZWxpdHJvbicKbmFtZTIgPSAndGV8NjI4MzE5OHw2Mjg0NDIxfDR8MTIyNHwtfEFUNFRFMjY3MTB8QVRSRVAxNXxSQy9IZWxpdHJvbicKCnNlcTEgPSB0ZS5mYXN0YVtbbmFtZTFdXQpzZXEyID0gdGUuZmFzdGFbW25hbWUyXV0KCnAgPSBkb3RwbG90KHNlcTEsIHNlcTIsIHdzaXplLCBubWF0Y2gpCgpwID0gcCArIGFubm90YXRlKCJ0ZXh0IiwgeCA9IC1JbmYsIHkgPSBJbmYsIGxhYmVsID0gcGFzdGUoJ3dzaXplPScsd3NpemUsJ1xubm1hdGNoPScsbm1hdGNoLCBzZXAgPSAnJyksIAogICAgICAgICAgICAgaGp1c3QgPSAtMC4xLCB2anVzdCA9IDEuMSkKCnAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdwYWlyd2lzZV8nLCcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCmBgYAoKIyMjIG9uZSBWUyBhbGwKYGBge3J9Cgp3c2l6ZSA9IDEwCm5tYXRjaCA9IDgKCm5hbWUwID0gJ3RlfDExNjgzNTY1fDExNjg5ODIxfDN8NjI1N3wrfEFUM1RFNDg1NDB8QVRDT1BJQTk1fExUUi9Db3BpYScKbmFtZTAgPSAndGV8MTY2OTE3NDh8MTY2OTUxNTR8MXwzNDA3fOKIknxBVDFURTU1MDcwfEFUQ09QSUE0MXxMVFIvQ29waWEnCm5hbWUwID0gZ3N1Yign4oiSJywgIi0iLCBuYW1lMCkKCgpvbmUudGUuZmFtID0gc3Ryc3BsaXQobmFtZTAsICdcXHwnKVtbMV1dWzhdCiMgb25lLnRlLmZhbSA9ICdCUk9EWUFHQTInCnF1ZXJ5LmZhbSA9IGRmLnF1ZXJ5JHF1ZXJ5W2RmLnF1ZXJ5JHN1YmZhbSA9PSBvbmUudGUuZmFtXQpxdWVyeS5mYW0gPSBxdWVyeS5mYW1bKHF1ZXJ5LmZhbSAlaW4lIHJlcy5uZXN0LnNpbSRWMSkgfCAocXVlcnkuZmFtICVpbiUgcmVzLm5lc3Quc2ltJFYyKV0KCm5hbWVzLmFsbCA9IHNldGRpZmYocXVlcnkuZmFtLCBuYW1lMCkKCnAuYWxsID0gbGlzdCgpCmZvcihuYW1lMiBpbiBuYW1lcy5hbGwpewogICMgbWVzc2FnZShuYW1lMikKICBzZXExID0gdGUuZmFzdGFbW25hbWUwXV0KICBzZXEyID0gdGUuZmFzdGFbW25hbWUyXV0KICAKICBzMSA9IHN0cnNwbGl0KG5hbWUwLCAnXFx8JylbWzFdXVs3XQogIHMyID0gc3Ryc3BsaXQobmFtZTIsICdcXHwnKVtbMV1dWzddCiAgcCA9IGRvdHBsb3Qoc2VxMSwgc2VxMiwgd3NpemUsIG5tYXRjaCkgKyB4bGFiKHMxKSArIHlsYWIoczIpCiAgcC5hbGxbW25hbWUyXV0gPSBwCn0KIyAKIyBwcCA9IGdyaWQuYXJyYW5nZShncm9icyA9IHAuYWxsLCBuY29sID0gMTMpICMjIGRpc3BsYXkgcGxvdAojIAojIAojIHBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdwYWlyd2lzZV9hbGwnLCcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUwLCBoZWlnaHQgPSA1MCkKIyBwcmludChwcCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKIyBkZXYub2ZmKCkKCnMwID0gcGFzdGUwKHN0cnNwbGl0KG5hbWUwLCAnXFx8JylbWzFdXVs3OjldLCBjb2xsYXBzZSA9ICdfJykKczAgPSBnc3ViKCIvIiwgIi0iLCBzMCkKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3BhaXJ3aXNlX2FsbF8nLHMwLCcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUwLCBoZWlnaHQgPSA1MCkKZ3JpZC5hcnJhbmdlKGdyb2JzID0gcC5hbGwsIG5jb2wgPSBjZWlsaW5nKHNxcnQobGVuZ3RoKHAuYWxsKSkpKSAjIFdyaXRlIHRoZSBncmlkLmFycmFuZ2UgaW4gdGhlIGZpbGUKZGV2Lm9mZigpICMgQ2xvc2UgdGhlIGZpbGUKCmBgYAoKCiMjIyBvbmUgY29ubmVjdGVkIGNvbXBvbmVudApgYGB7cn0KCndzaXplID0gMTAKbm1hdGNoID0gOAoKCm5hbWUwID0gJ3RlfDYyMDU2MjF8NjIwNjE4NHwyfDU2NHziiJJ8QVQyVEUyNTI1NXxIRUxJVFJPTlkxRHxSQy9IZWxpdHJvbicKbmFtZTAgPSAndGV8MTQxODkyNTZ8MTQxOTAyNjZ8NXwxMDExfC18QVQ1VEU1MDcwMHxIRUxJVFJPTlkzfFJDL0hlbGl0cm9uJwpuYW1lMCA9ICd0ZXwxMjUxMzIzOXwxMjUxMzgyNHwxfDU4NnwrfEFUMVRFNDA3MjV8QVRISUxBNEF8TFRSL0d5cHN5JwpuYW1lMCA9ICd0ZXwxMTY0NzQyNnwxMTY0ODkxMnwxfDE0ODd8K3xBVDFURTM3NzA1fEFUUkVQN3xSQy9IZWxpdHJvbicKbmFtZTAgPSAndGV8MTE2ODM1NjV8MTE2ODk4MjF8M3w2MjU3fCt8QVQzVEU0ODU0MHxBVENPUElBOTV8TFRSL0NvcGlhJwpuYW1lMCA9IGdzdWIoJ+KIkicsICItIiwgbmFtZTApCgoKbmFtZXMuYWxsID0gdW5pcXVlKGMocmVzLm5lc3Quc2ltJFYxW3Jlcy5uZXN0LnNpbSRWOCA9PSBuYW1lMF0sCiAgICAgICAgICAgICAgICAgICAgIHJlcy5uZXN0LnNpbSRWOFtyZXMubmVzdC5zaW0kVjEgPT0gbmFtZTBdKSkKIyBuYW1lcy5hbGwgPSB1bmlxdWUoYyhyZXMubmVzdCRWMVtyZXMubmVzdCRWOCA9PSBuYW1lMF0sIAojICAgICAgICAgICAgICAgICAgICAgIHJlcy5uZXN0JFY4W3Jlcy5uZXN0JFYxID09IG5hbWUwXSkpCgpwLmFsbCA9IGxpc3QoKQpmb3IobmFtZTIgaW4gbmFtZXMuYWxsKXsKICAjIG1lc3NhZ2UobmFtZTIpCiAgc2VxMSA9IHRlLmZhc3RhW1tuYW1lMF1dCiAgc2VxMiA9IHRlLmZhc3RhW1tuYW1lMl1dCiAgCiAgczEgPSBwYXN0ZTAoc3Ryc3BsaXQobmFtZTAsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ3wnKQogIHMyID0gcGFzdGUwKHN0cnNwbGl0KG5hbWUyLCAnXFx8JylbWzFdXVs3OjldLCBjb2xsYXBzZSA9ICd8JykKICBwID0gZG90cGxvdChzZXExLCBzZXEyLCB3c2l6ZSwgbm1hdGNoKSArIHhsYWIoczEpICsgeWxhYihzMikKICBwLmFsbFtbbmFtZTJdXSA9IHAKfQoKCnMwID0gcGFzdGUwKHN0cnNwbGl0KG5hbWUwLCAnXFx8JylbWzFdXVs3OjldLCBjb2xsYXBzZSA9ICdfJykKczAgPSBnc3ViKCIvIiwgIi0iLCBzMCkKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3BhaXJ3aXNlX2Nvbm5lY3RfJyxzMCwnLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1MCwgaGVpZ2h0ID0gNTApCmdyaWQuYXJyYW5nZShncm9icyA9IHAuYWxsLCBuY29sID0gY2VpbGluZyhzcXJ0KGxlbmd0aChwLmFsbCkpKSkgIyBXcml0ZSB0aGUgZ3JpZC5hcnJhbmdlIGluIHRoZSBmaWxlCmRldi5vZmYoKSAjIENsb3NlIHRoZSBmaWxlCgpgYGAKCgoKYGBge3J9CgoKbmFtZTEgPSAndGV8MTQxODkyNTZ8MTQxOTAyNjZ8NXwxMDExfC18QVQ1VEU1MDcwMHxIRUxJVFJPTlkzfFJDL0hlbGl0cm9uJwpuYW1lMiA9ICd0ZXwyMTYyMjk1fDIxNjI5Mzd8Mnw2NDN8LXxBVDJURTA5OTUwfEhFTElUUk9OWTN8UkMvSGVsaXRyb24nCm5hbWUwID0gbmFtZTEKCm5hbWVzLmFsbCA9IHVuaXF1ZShjKHJlcy5uZXN0JFYxW3Jlcy5uZXN0JFY4ID09IG5hbWUwXSwgcmVzLm5lc3QkVjhbcmVzLm5lc3QkVjEgPT0gbmFtZTBdKSkKCgpuYW1lcyA9IGMobmFtZTEsIG5hbWUyKQpiLnRtcCA9IGJsLnJlc1soYmwucmVzJFYxICVpbiUgbmFtZXMpICYgKGJsLnJlcyRWOCAlaW4lIG5hbWVzKSxdCgpyZXMubmVzdFsocmVzLm5lc3QkVjEgJWluJSBuYW1lcykgJiAocmVzLm5lc3QkVjggJWluJSBuYW1lcyksIF0KCmBgYAoKCiMgU1ZzCiMjIFJlYWRpbmcgbmVzdGVkbmVzcwpgYGB7cn0KCiMgTG9hZCBzaW1pbGFyaXR5IGZ1bmN0aW9uCgpmaWxlLm5lc3RlZG5lc3MgPSBwYXN0ZShwYXRoLndvcmssICdzdl9iaWdfb25fYmlnX25lc3QucmRzJywgc2VwID0gJycpCgoKaWYoIWZpbGUuZXhpc3RzKGZpbGUubmVzdGVkbmVzcykpewogIGJsLmZpbGUgPSBwYXN0ZShwYXRoLndvcmssICdzdl9iaWdfb25fYmlnLnR4dCcsIHNlcCA9ICcnKQogIGJsLnJlcyA9IHJlYWQudGFibGUoYmwuZmlsZSkKICBibC5yZXMgPSBibC5yZXNbYmwucmVzJFYxICE9IGJsLnJlcyRWOCxdCgogIHJlcy5uZXN0ID0gZmluZE5lc3RlZG5lc3MoYmwucmVzLCB1c2Uuc3RyYW5kID0gRikKICAgIAogIHJlcy5uZXN0JGxlbjEgPSByZXMubmVzdC5sZW5bcmVzLm5lc3QkVjFdCiAgcmVzLm5lc3QkbGVuOCA9IHJlcy5uZXN0LmxlbltyZXMubmVzdCRWOF0KICByZXMubmVzdCRwMSA9IHJlcy5uZXN0JEMxIC8gcmVzLm5lc3QkbGVuMQogIHJlcy5uZXN0JHA4ID0gcmVzLm5lc3QkQzggLyByZXMubmVzdCRsZW44ICAKICBzYXZlUkRTKHJlcy5uZXN0LCBmaWxlLm5lc3RlZG5lc3MsIGNvbXByZXNzID0gRikKfSBlbHNlIHsKICByZXMubmVzdCA9IHJlYWRSRFMoZmlsZS5uZXN0ZWRuZXNzKQp9CgpyZXMubmVzdC5sZW4gPSBzYXBwbHkodW5pcXVlKGMocmVzLm5lc3QkVjEsIHJlcy5uZXN0JFY4KSksIAogICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24ocykgYXMubnVtZXJpYyhzdHJzcGxpdChzLCAnXFx8JylbWzFdXVsyXSkpCnJlcy5uZXN0MCA9IHJlcy5uZXN0CgoKCmBgYAoKCiMjIFRFIHN0YXQKYGBge3J9CnJlcy5uZXN0ID0gcmVzLm5lc3QwCgpzdi5zZSA9IHJlYWRSRFMocGFzdGUocGF0aC5zdnMsICdzdl9zZS5yZHMnLCBzZXAgPSAnJykpCgpzdi5zZS5sZW4gPSBzdi5zZVtzdi5zZSRsZW4gPj0gMTAwLF0Kc3Yuc2UubGVuJGluLmNvbm5lY3QgPSBzdi5zZS5sZW4kbmFtZSAlaW4lIG5hbWVzKHJlcy5uZXN0LmxlbikKCmNudC5zdi5zZSA9IHRhYmxlKHN2LnNlLmxlbiRpbi5jb25uZWN0ICwgc3Yuc2UubGVuJHRlKQpjbnQuc3Yuc2UKCmRmID0gcmVzaGFwZTI6Om1lbHQoY250LnN2LnNlKQoKdGUuY29udGVudC5uYW1lcyA9IGMoIm5vVEUiLCAiaXNURSIsICJoYXNURSIsICJoYXNURXBhcnQiLCAiaXNURXBhcnQiKQpjb2xzID0gYygnI0Q4RDlDRicsICcjRUI0NTVGJywgJyM3QjYwNzknLCAnIzNDOERBRCcsICcjNzlCNzczJykKbmFtZXMoY29scykgPSB0ZS5jb250ZW50Lm5hbWVzCgpkZiRWYXIyID0gZmFjdG9yKGRmJFZhcjIsIGxldmVscyA9IHJldihjKCdpc1RFJywgJ2lzVEVwYXJ0JywgJ2hhc1RFJywgJ2hhc1RFcGFydCcsICdub1RFJykpKQoKCnAgPSBnZ3Bsb3QoZGYsIGFlcyh4ID0gVmFyMiwgeSA9IHZhbHVlLCBmaWxsID0gVmFyMiwgYWxwaGEgPSBWYXIxLCBjb2xvciA9IFZhcjEpKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiLCB3aWR0aCA9IDAuOCkgKwogIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBjKDAuOCwgMSksIGxhYmVscyA9IGMoIk5vIiwgIlllcyIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ3RyYW5zcGFyZW50JywgJ2JsYWNrJyksIGxhYmVscyA9IGMoIm5vdCBpbiBncmFwaCIsICJpbiBncmFwaCIpKSArCiAgbGFicyhmaWxsID0gIiIsIGNvbG9yPSdDb25uZWN0ZWQgdG8gb3RoZXJzJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbHMpICsKICB4bGFiKCIiKSArCiAgeWxhYigiTnVtYmVyIG9mIFNWcyIpICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBndWlkZXMoYWxwaGEgPSAibm9uZSIsIGZpbGwgPSAnbm9uZScpICsKICB0aGVtZV9taW5pbWFsKCkgKyBjb29yZF9mbGlwKCkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjcsIDAuMyksICAgICAjIEFkanVzdCB0aGVzZSBjb29yZGluYXRlcyBhcyBuZWVkZWQKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9InRyYW5zcGFyZW50IiwgY29sb3I9J2dyZXk3MCcpICAjIE1ha2VzIHRoZSBsZWdlbmQgYmFja2dyb3VuZCB0cmFuc3BhcmVudAogICkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGZpbGwgPSAiI0FFQzNBRSIpKSkgICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKcAoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2JfaW5fZ3JhcGgucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDMuNSwgaGVpZ2h0ID0gNCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCmBgYAoKIyMgVEUgZmFtaWxpZXMgaW4gU1YgdHlwZXMKYGBge3J9CnN2LnNlLmxlbiA9IHN2LnNlW3N2LnNlJGxlbiA+PSAxMDAsXQpjbnQuZmFtLnN2ID0gdGFibGUoc3Yuc2UubGVuJGZhbVtzdi5zZS5sZW4kZmFtIT0nJ10sIHN2LnNlLmxlbiR0ZVtzdi5zZS5sZW4kZmFtIT0nJ10pCmNudC5mYW0uc3YgPSB0KGFwcGx5KGNudC5mYW0uc3YsIDEsIGZ1bmN0aW9uKHgpIHgvc3VtKHgpKSkKY250LmZhbS5zdiA9IHJlc2hhcGUyOjptZWx0KGNudC5mYW0uc3YpCgpwID0gZ2dwbG90KGNudC5mYW0uc3YsIGFlcyh4ID0gVmFyMiwgeSA9IFZhcjEsIGNvbG9yID0gVmFyMikpICsgCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IHZhbHVlLCBhbHBoYSA9IHZhbHVlICogMikpICsgdGhlbWVfbWluaW1hbCgpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbHMpICArCiAgZ2VvbV90ZXh0KGRhdGEgPSBjbnQuZmFtLnN2W2NudC5mYW0uc3YkdmFsdWUgPj0gMC4yLF0sIAogICAgICAgICAgICAgIGFlcyh4PVZhcjIsIHk9VmFyMSwgbGFiZWwgPSByb3VuZCh2YWx1ZSwgMikpLCAKICAgICAgICAgICAgICBzaXplID0gMi41LCBjb2xvciA9ICdibGFjaycsIAogICAgICAgICAgICBudWRnZV94ID0gMC4zLAogICAgICAgICAgICBudWRnZV95ID0gMCkgKwogIGd1aWRlcyhzaXplID0gIm5vbmUiLCBhbHBoYSA9ICJub25lIiwgY29sb3IgPSAnbm9uZScpICsKICB4bGFiKCdTViB0eXBlJykgKyB5bGFiKCdURSBmYW1pbHknKQpwCgoKY250LmZhbS5zdiA9IHJvd1N1bXModGFibGUoc3Yuc2UubGVuJGZhbVtzdi5zZS5sZW4kZmFtIT0nJ10sIHN2LnNlLmxlbiR0ZVtzdi5zZS5sZW4kZmFtIT0nJ10pKQpjbnQuZmFtLnN2ID0gZGF0YS5mcmFtZSh2YWx1ZSA9IGNudC5mYW0uc3YsIG5hbWVzID0gbmFtZXMoY250LmZhbS5zdikpCnJvd25hbWVzKGNudC5mYW0uc3YpID0gTlVMTAoKZyA9IGdncGxvdChjbnQuZmFtLnN2LCBhZXMoeCA9IG5hbWVzLCB5ID0gdmFsdWUpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBmaWxsPSJncmV5ODAiKSsKICBjb29yZF9mbGlwKCkgKyB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGFzdGUoIjFlIixzZXEoMCw0LDEpLCBzZXAgPSAnJyksIGJyZWFrcz0gc2VxKDAsNCwxKSoxMDAwKSArCiAgeWxhYignIycpICsgZ2VvbV90ZXh0KGFlcyhsYWJlbD12YWx1ZSwgeT0wKSwgaGp1c3Q9MCwgc2l6ZSA9IDIuNSkKZyAKCgpwcCA9IGdncHVicjo6Z2dhcnJhbmdlKHAsIGcsIG5jb2wgPSAyLCB3aWR0aHMgPSBjKDAuNzUsIDAuMjUpKQpwcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl90ZV9mYW1fc3ZfdHlwZS5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKcHJpbnQocHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCiMgSW5zZXJ0aW9uIGFuZCBkZWxldGlvbgppZHggPSAoc3Yuc2UubGVuJGZhbSE9JycpICYgKHN2LnNlLmxlbiRmcmVxLm1heCA8PSAzKQp0YWJsZShzdi5zZS5sZW4kZmFtW2lkeF0sIHN2LnNlLmxlbiR0ZVtpZHhdKQoKaWR4ID0gKHN2LnNlLmxlbiRmYW0hPScnKSAmIChzdi5zZS5sZW4kZnJlcS5tYXggPj0gMjUpICYgKHN2LnNlLmxlbiRsZW4gPj0gMTAwKSAKdGFibGUoc3Yuc2UubGVuJGZhbVtpZHhdLCBzdi5zZS5sZW4kdGVbaWR4XSkKYGBgCgojIyBGaWx0cmF0aW9uCmBgYHtyfQoKcmVzLm5lc3QgPSByZXMubmVzdDAKCnN2Lm5hbWVzLm1peCA9IHN2LnNlJG5hbWVbc3Yuc2UkZmFtID09ICdNaXgnXQpyZXMubmVzdCA9IHJlcy5uZXN0WyEocmVzLm5lc3QkVjEgJWluJSBzdi5uYW1lcy5taXgpLF0KcmVzLm5lc3QgPSByZXMubmVzdFshKHJlcy5uZXN0JFY4ICVpbiUgc3YubmFtZXMubWl4KSxdCgoKc3YubmFtZXMubWl4ID0gc3Yuc2UkbmFtZVtzdi5zZSR0ZSA9PSAnbm9URSddCnJlcy5uZXN0ID0gcmVzLm5lc3RbIShyZXMubmVzdCRWMSAlaW4lIHN2Lm5hbWVzLm1peCksXQpyZXMubmVzdCA9IHJlcy5uZXN0WyEocmVzLm5lc3QkVjggJWluJSBzdi5uYW1lcy5taXgpLF0KCnNpbmdsZXRvbi5tb2RlID0gRgppZihzaW5nbGV0b24ubW9kZSl7CiAgc3YubmFtZXMuZnJlcSA9IHN2LnNlJG5hbWVbc3Yuc2UkZnJlcS5tYXggPD0gM10KICAjIHN2Lm5hbWVzLmZyZXEgPSBzdi5zZSRuYW1lW3N2LnNlJGZyZXEubWF4ID49IDI1XQogIHJlcy5uZXN0ID0gcmVzLm5lc3RbcmVzLm5lc3QkVjEgJWluJSBzdi5uYW1lcy5mcmVxLF0KICByZXMubmVzdCA9IHJlcy5uZXN0W3Jlcy5uZXN0JFY4ICVpbiUgc3YubmFtZXMuZnJlcSxdCn0KCnByZWZpeC5tb2RlID0gYygnJywgJ19zaW5nbGUnKQoKCmBgYAoKCiMjIEdyYXBoCmBgYHtyfQojIGFsbCBlZGdlcwppZHggPSByZXMubmVzdCRwMSA+PSBzaW0uY3V0b2ZmCmVkZ2VzID0gY2JpbmQocmVzLm5lc3QkVjFbaWR4XSwgcmVzLm5lc3QkVjhbaWR4XSkKaWR4ID0gcmVzLm5lc3QkcDggPj0gc2ltLmN1dG9mZgplZGdlcyA9IHJiaW5kKGVkZ2VzLCBjYmluZChyZXMubmVzdCRWOFtpZHhdLCByZXMubmVzdCRWMVtpZHhdKSkKdGUuZW5nZXMubmFtZXMgPSB1bmlxdWUoYyhlZGdlc1ssMV0sIGVkZ2VzWywyXSkpCgp0bXAgPSBzdi5zZSR0ZQpuYW1lcyh0bXApID0gc3Yuc2UkbmFtZQp0ZS5lbmdlcy50eXBlID0gdG1wW3RlLmVuZ2VzLm5hbWVzXQoKdG1wID0gc3Yuc2UkZmFtCm5hbWVzKHRtcCkgPSBzdi5zZSRuYW1lCnRlLmVuZ2VzLmZhbSA9IHRtcFt0ZS5lbmdlcy5uYW1lc10KCiMgbm9kZXMKaWR4ID0gKHJlcy5uZXN0JHAxID49IHNpbS5jdXRvZmYpICYgKHJlcy5uZXN0JHA4ID49IHNpbS5jdXRvZmYpCnRlLm5vZGVzID0gY2JpbmQocmVzLm5lc3QkVjFbaWR4XSwgcmVzLm5lc3QkVjhbaWR4XSkKdGUucmVzdCA9IHNldGRpZmYodGUuZW5nZXMubmFtZXMsIGModGUubm9kZXNbLDFdLCB0ZS5ub2Rlc1ssMl0pKQoKCnRlLm5vZGVzLmdyYXBoIDwtIGlncmFwaDo6bWFrZV9ncmFwaCh0KHRlLm5vZGVzKSwgZGlyZWN0ZWQgPSBUKQp0ZS5ub2Rlcy5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLm5vZGVzLmdyYXBoKQp0ZS5ub2Rlcy5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0ZS5ub2Rlcy5ncmFwaCkKCm5vZGVzID0gZGF0YS5mcmFtZShub2RlID0gcGFzdGUoJ04nLCB0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXAsIHNlcCA9ICcnKSwgdGUgPSBuYW1lcyh0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXApKQoKbm9kZXMucmVzdCA9IGRhdGEuZnJhbWUobm9kZSA9IHBhc3RlKCdSJywgKDE6bGVuZ3RoKHRlLnJlc3QpKSwgc2VwID0gJycpLCB0ZSA9IHRlLnJlc3QpCm5vZGVzID0gcmJpbmQobm9kZXMsIG5vZGVzLnJlc3QpCgpyb3duYW1lcyhub2RlcykgPSBub2RlcyR0ZQoKIyBEZWZpbmUgVEUgdHlwZQpub2Rlcy5jbnQgPSBkYXRhLmZyYW1lKGNudCA9IGModGFibGUobm9kZXMkbm9kZSkpKQpub2Rlcy5jbnQkbm9kZSA9IHJvd25hbWVzKG5vZGVzLmNudCkKbm9kZXMuY250JHR5cGUgPSBzYXBwbHkobm9kZXMuY250JG5vZGUsIGZ1bmN0aW9uKHMpewogIHMudGUgPSBub2RlcyR0ZVtub2RlcyRub2RlID09IHNdCiAgdHlwZS50ZSA9IHVuaXF1ZSh0ZS5lbmdlcy50eXBlW3MudGVdKQogIGlmKGxlbmd0aCh0eXBlLnRlKSA9PSAxKXsKICAgIHJldHVybih0eXBlLnRlKQogIH0gZWxzZSB7CiAgICB0eXBlLnRlID0gdGFibGUodHlwZS50ZSkKICAgIHR5cGUudGUgPSBuYW1lcyh0eXBlLnRlKVt0eXBlLnRlID09IG1heCh0eXBlLnRlKV0KICAgIHJldHVybih0eXBlLnRlWzFdKQogIH0KfSkKdGFibGUobm9kZXMuY250JHR5cGUpCgojIERlZmluZSBURSBmYW1pbHkKbm9kZXMuY250JGZhbSA9IHNhcHBseShub2Rlcy5jbnQkbm9kZSwgZnVuY3Rpb24ocyl7CiAgcy50ZSA9IG5vZGVzJHRlW25vZGVzJG5vZGUgPT0gc10KICB0eXBlLnRlID0gdW5pcXVlKHRlLmVuZ2VzLmZhbVtzLnRlXSkKICBpZihsZW5ndGgodHlwZS50ZSkgPT0gMSl7CiAgICByZXR1cm4odHlwZS50ZSkKICB9IGVsc2UgewogICAgdHlwZS50ZSA9IHRhYmxlKHR5cGUudGUpCiAgICB0eXBlLnRlID0gbmFtZXModHlwZS50ZSlbdHlwZS50ZSA9PSBtYXgodHlwZS50ZSldCiAgICByZXR1cm4odHlwZS50ZVsxXSkKICB9Cn0pCnRhYmxlKG5vZGVzLmNudCRmYW0pCgoKIyBSZWRlZmluZSBlZGdlcyBidXQgd2l0aCBub2RlIG5hbWVzCmlkeC5lbmRlcyA9IChlZGdlc1ssMV0gJWluJSBub2RlcyR0ZSkgJiAoZWRnZXNbLDJdICVpbiUgbm9kZXMkdGUpCmIuZ3JhcGggPSBjYmluZChub2Rlc1tlZGdlc1tpZHguZW5kZXMsMV0sICdub2RlJ10sbm9kZXNbZWRnZXNbaWR4LmVuZGVzLDJdLCAnbm9kZSddKQpiLmdyYXBoID0gdW5pcXVlKGIuZ3JhcGgpCiMgYi5ncmFwaCA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gIT0gYi5ncmFwaFssMl0sXQpiLmdyYXBoLnVuaSA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gPT0gYi5ncmFwaFssMl0sXQpiLmdyYXBoID0gYi5ncmFwaFtiLmdyYXBoWywxXSAhPSBiLmdyYXBoWywyXSxdCgpsZW5ndGgodW5pcXVlKGMoYi5ncmFwaFssMV0sIGIuZ3JhcGhbLDJdKSkpCgojIHJlZHVjZSBpbmRpcmVjdCBhcnJvd3MKaWR4LnJlbW92ZSA9IGMoKQpmb3IoaS5lZGdlIGluIDE6bnJvdyhiLmdyYXBoKSl7CiAgaWYoaS5lZGdlICUlIDEwMDAgPT0gMCkgcHJpbnQoaS5lZGdlKQogIHRtcC50byA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gPT0gYi5ncmFwaFtpLmVkZ2UsMV0sMl0KICB0bXAuZnJvbSA9IGIuZ3JhcGhbYi5ncmFwaFssMl0gPT0gYi5ncmFwaFtpLmVkZ2UsMl0sMV0KICBpZihsZW5ndGgoaW50ZXJzZWN0KHRtcC50bywgdG1wLmZyb20pKSA+IDApIGlkeC5yZW1vdmUgPSBjKGlkeC5yZW1vdmUsIGkuZWRnZSkKfQppZHgucmVtb3ZlID0gdW5pcXVlKGlkeC5yZW1vdmUpCmIuZ3JhcGggPSBiLmdyYXBoWy1pZHgucmVtb3ZlLF0KIyBiLmdyYXBoID0gcmJpbmQoYi5ncmFwaCwgYi5ncmFwaC51bmkpCgojIFByaW50IGdyYXBoCgpnLm5vZGVzLnR5cGUgPSBub2Rlcy5jbnQkdHlwZQpuYW1lcyhnLm5vZGVzLnR5cGUpID0gbm9kZXMuY250JG5vZGUKZy5ub2Rlcy5jbnQgPSBub2Rlcy5jbnQkY250Cm5hbWVzKGcubm9kZXMuY250KSA9IG5vZGVzLmNudCRub2RlCmcubm9kZXMuZmFtID0gbm9kZXMuY250JGZhbQpuYW1lcyhnLm5vZGVzLmZhbSkgPSBub2Rlcy5jbnQkbm9kZQoKCmcuY29scy5uYW1lcyA9IGMoIm5vVEUiLCAiaXNURSIsICJoYXNURSIsICJoYXNURXBhcnQiLCAiaXNURXBhcnQiKQpnLmNvbHMgPSBjKCcjRkZEOTY2JywgJyNFQjQ1NUYnLCAnIzdCNjA3OScsICcjM0M4REFEJywgJyM3OUI3NzMnKQpuYW1lcyhnLmNvbHMpID0gZy5jb2xzLm5hbWVzCgoKZy5wYXJ0IDwtIG5ldHdvcmsoYi5ncmFwaCwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQpCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMudHlwZVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgIyBhcnJvdy5nYXAgPSAwLCAKICAgICAgICAgICAgIyBhcnJvdy5zaXplID0gMywKICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scykgKyBndWlkZXMoc2l6ZSA9IEYpCnAKCiMgcGF0aC5maWd1cmVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvJwpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2FsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfdHlwZS5wZGYnLCBzZXAgPSAnJyksIAogICAgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgojIHNldC5zZWVkKDIwKQojIHAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImdyZXkzMCIsIAojICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKIyAgICAgICAgICAgICBjb2xvciA9IGMoJ1RFJywgJ25vVEUnKVsoZy5ub2Rlcy50eXBlW2IuZ3JhcGgubmFtZXNdID09ICdub1RFJykqMSsxXSwKIyAgICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAojICAgICAgICAgICAgICMgYXJyb3cuZ2FwID0gMCwgCiMgICAgICAgICAgICAgIyBhcnJvdy5zaXplID0gMywKIyAgICAgICAgICAgICBwYWxldHRlID0gYygnbm9URScgPSAnYmxhY2snLCAnVEUnID0gJyNBRUMzQUUnKSkgKyBndWlkZXMoc2l6ZSA9IEYpCiMgcAojIAojICMgcGF0aC5maWd1cmVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvJwojIHBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2JfYWxsX2NsdXN0ZXInLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ190eXBlLnBkZicsIHNlcCA9ICcnKSwgCiMgICAgIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKIyBwcmludChwKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgojIGRldi5vZmYoKQoKYGBgCgoKIyMjIENvbG9yZWQgYnkgVEUgZmFtaWx5CmBgYHtyfQpnLmZhbS5uYW1lcyA9IHNvcnQodW5pcXVlKGcubm9kZXMuZmFtKSkKIyBmYW0ucGFsZXR0ZSA8LSB2aXJpZGlzX3BhbChvcHRpb24gPSAiRCIpKGxlbmd0aChnLmZhbS5uYW1lcykpICAKZmFtLnBhbGV0dGUgPC0gcmFpbmJvdyhsZW5ndGgoZy5mYW0ubmFtZXMpKQpuYW1lcyhmYW0ucGFsZXR0ZSkgPSBnLmZhbS5uYW1lcwoKIyAtLS0tIE5ldyBjb2xvcnMgZm9yIFRFIGZhbWlsaWVzCmZhbS5wYWxldHRlID0gYygpCmlkeC5wYWxsZXRlID0gYygpCgppZHguZmFtIDwtIGdyZXAoIl5IZWxpdHJvbiIsIGcuZmFtLm5hbWVzLCB2YWx1ZSA9IEZBTFNFKQp0bXAucGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGMoJyNCRkFDRTInLCAnIzI2NkQ5OCcsICcjNDIyQjcyJykpKGxlbmd0aChpZHguZmFtKSkKaWR4LnBhbGxldGUgPSBjKGlkeC5wYWxsZXRlLCBpZHguZmFtKQpmYW0ucGFsZXR0ZSA9IGMoZmFtLnBhbGV0dGUsIHRtcC5wYWxldHRlKQoKaWR4LmZhbSA8LSBncmVwKCJeTFRSIiwgZy5mYW0ubmFtZXMsIHZhbHVlID0gRkFMU0UpCnRtcC5wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygnI0JGREIzOCcsICcjNTRCNDM1JykpKGxlbmd0aChpZHguZmFtKSkKaWR4LnBhbGxldGUgPSBjKGlkeC5wYWxsZXRlLCBpZHguZmFtKQpmYW0ucGFsZXR0ZSA9IGMoZmFtLnBhbGV0dGUsIHRtcC5wYWxldHRlKQoKaWR4LmZhbSA8LSBncmVwKCJeRE5BIiwgZy5mYW0ubmFtZXMsIHZhbHVlID0gRkFMU0UpCnRtcC5wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygnI0Y5QjVEMCcsICcjOTcxNTQ5JykpKGxlbmd0aChpZHguZmFtKSkKaWR4LnBhbGxldGUgPSBjKGlkeC5wYWxsZXRlLCBpZHguZmFtKQpmYW0ucGFsZXR0ZSA9IGMoZmFtLnBhbGV0dGUsIHRtcC5wYWxldHRlKQoKaWR4LmZhbSA9IHNldGRpZmYoMTpsZW5ndGgoZy5mYW0ubmFtZXMpLCBpZHgucGFsbGV0ZSkKdG1wLnBhbGV0dGUgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCcjRkZDMjZGJywgJyNDMzgxNTQnLCAnIzg4NEEzOScsICcjNEUzNjM2JykpKGxlbmd0aChpZHguZmFtKSkKaWR4LnBhbGxldGUgPSBjKGlkeC5wYWxsZXRlLCBpZHguZmFtKQpmYW0ucGFsZXR0ZSA9IGMoZmFtLnBhbGV0dGUsIHRtcC5wYWxldHRlKQoKbmFtZXMoZmFtLnBhbGV0dGUpID0gZy5mYW0ubmFtZXNbaWR4LnBhbGxldGVdCmZhbS5wYWxldHRlWydVbmFzc2lnbmVkJ10gPSAnZ3JleScKZmFtLnBhbGV0dGVbJ01peCddID0gJ2JsYWNrJwoKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgICMgYXJyb3cuZ2FwID0gMCwgCiAgICAgICAgICAgICMgYXJyb3cuc2l6ZSA9IDMsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgPSBwICsgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLCAKICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41LCAiY20iKSkgKyBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKQpwCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfZmFtaWx5LnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2NsdXN0ZXInLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19mYW1pbHlfbGVnZW5kLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA3LCBoZWlnaHQgPSA1KQpwcmludChwKyBjb29yZF9maXhlZChyYXRpbyA9IDEpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgpgYGAKCgojIyMgTm9kZSBzaXplIGRpc3RyaWJ1dGlvbgpgYGB7cn0KZGYgPSBkYXRhLmZyYW1lKG5vZGUgPSB1bmlxdWUobm9kZXMkbm9kZSkpCmRmJHNpemUgPSBnLm5vZGVzLmNudFtkZiRub2RlXQpkZiRmYW0gPSBnLm5vZGVzLmZhbVtkZiRub2RlXQpkZiR0eXBlID0gZy5ub2Rlcy50eXBlW2RmJG5vZGVdCgpmYW0ucGFsZXR0ZQoKcCA9IGdncGxvdChkZiwgYWVzKHggPSB0eXBlLCB5ID0gc2l6ZSwgY29sb3I9ZmFtKSkgKwogIGdlb21faml0dGVyKHdpZHRoID0gMC4yKSArCiAgbGFicyh4ID0gIlR5cGUiLCB5ID0gIlNpemUiKSArIAogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBmYW0ucGFsZXR0ZSkrCiAgdGhlbWVfbWluaW1hbCgpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKSArCiAgbGFicyhjb2xvciA9ICJURSBmYW1pbHkiKSArIHhsYWIoJycpICsgeWxhYignTm9kZSBzaXplIChOdW1iZXIgb2Ygc2ltaWxhciBTVnMpJykKcAoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2Jfc2l6ZV9kaXN0cmlidXRpb24ucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDYuNSwgaGVpZ2h0ID0gNCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpgYGAKCgoKIyMjIFNlcGFyYXRlbHkgdmlzdWFsaXNlIGNvbm5lY3RlZCBjb21wb25lbnRzCmBgYHtyfQp0bXAuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoYi5ncmFwaCksIGRpcmVjdGVkID0gVCkKdG1wLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodG1wLmdyYXBoKQp0bXAuY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHModG1wLmdyYXBoKQoKdG1wLmNudCA9IHRhYmxlKHRtcC5jb21wJG1lbWJlcnNoaXApCnRtcC5jbnQgPSAtc29ydCgtdG1wLmNudCkKaGVhZCh0bXAuY250KQoKayA9IDEKdG1wLmsgPSBhcy5udW1lcmljKG5hbWVzKHRtcC5jbnQpW2tdKQp0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwID09IHRtcC5rXQpiLmdyYXBoLnN1YiA9IGIuZ3JhcGhbKGIuZ3JhcGhbLDFdICVpbiUgdG1wLm5hbWVzKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAoYi5ncmFwaFssMl0gJWluJSB0bXAubmFtZXMpLF0KCmcucGFydC5zdWIuYmlnIDwtIG5ldHdvcmsoYi5ncmFwaC5zdWIsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzLnN1Yi5iaWcgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQuc3ViLmJpZykKCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5iaWcsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLnR5cGVbYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwKICAgICAgICAgICAgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgIHBhbGV0dGUgPSBnLmNvbHMpICsgZ3VpZGVzKHNpemUgPSBGKQpwLmJpZy50eXBlID0gcCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQuc3ViLmJpZywgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sCiAgICAgICAgICAgIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKQpwLmJpZy5jb2xvciA9IHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKdG1wLmsgPSBhcy5udW1lcmljKG5hbWVzKHRtcC5jbnQpW2tdKQp0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwICE9IHRtcC5rXQpiLmdyYXBoLnN1YiA9IGIuZ3JhcGhbKGIuZ3JhcGhbLDFdICVpbiUgdG1wLm5hbWVzKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAoYi5ncmFwaFssMl0gJWluJSB0bXAubmFtZXMpLF0KCmcucGFydC5zdWIuc21hbGwgPC0gbmV0d29yayhiLmdyYXBoLnN1YiwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMuc3ViLnNtYWxsID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0LnN1Yi5zbWFsbCkKCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5zbWFsbCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy50eXBlW2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwKICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scykgKyBndWlkZXMoc2l6ZSA9IEYpCnAuc21hbGwudHlwZSA9cCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQuc3ViLnNtYWxsLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sCiAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCnAuc21hbGwuY29sb3IgPSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKCgpgYGAKCiMjIyMgU2F2ZQpgYGB7cn0KIyBwLmJpZy50eXBlCiMgcC5iaWcuY29sb3IKIyBwLnNtYWxsLnR5cGUKIyBwLnNtYWxsLmNvbG9yCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9iaWdfY2x1c3RlcicsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX3R5cGUucGRmJywgc2VwID0gJycpLCAKICAgIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocC5iaWcudHlwZSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2JpZ19jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfZmFtaWx5LnBkZicsIHNlcCA9ICcnKSwgCiAgICB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHAuYmlnLmNvbG9yKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2Jfc21hbGxfY2x1c3RlcicsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX3R5cGUucGRmJywgc2VwID0gJycpLCAKICAgIHdpZHRoID0gNiwgaGVpZ2h0ID0gNikKcHJpbnQocC5zbWFsbC50eXBlKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2Jfc21hbGxfY2x1c3RlcicsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX2ZhbWlseS5wZGYnLCBzZXAgPSAnJyksIAogICAgd2lkdGggPSA2LCBoZWlnaHQgPSA2KQpwcmludChwLnNtYWxsLmNvbG9yKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgoKYGBgCgoKCgojIyMgUnVuIGJ5IGFjY2Vzc2lvbnMKYGBge3J9CnBhdGguZmlndXJlcy5hY2MgPSAnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3RlL2ZpZ3VyZXNfdGVncmFwaF9hY2Nlc3Npb25zLycKc3YuYmluID0gcmVhZC50YWJsZSgnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3N2L3N2c19zZV9iaW5fdjAzLnR4dCcsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGLCBjaGVjay5uYW1lcyA9IEZBTFNFKQpgYGAKCgpgYGB7cn0KIyBhY2MgPSAnMTAwMDInCgpmb3IoYWNjIGluIGNvbG5hbWVzKHN2LmJpbikpewogIHN2LmFjYyA9IHJvd25hbWVzKHN2LmJpbilbc3YuYmluWyxhY2NdID09IDFdCiAgcm93bmFtZXMoc3Yuc2UpID0gc3Yuc2UkZ3IKICBzdi5hY2MgPSBzdi5zZVtzdi5hY2MsICduYW1lJ10KICAKICBzdi5hY2MgPSBpbnRlcnNlY3Qoc3YuYWNjLCByb3duYW1lcyhub2RlcykpCiAgbm9kZXMuY250LmFjYyA9IHRhYmxlKG5vZGVzW3N2LmFjYywnbm9kZSddKQogIAogIAogIHN2LmFscGhhID0gcmVwKDAsIGxlbmd0aChiLmdyYXBoLm5hbWVzKSkKICBuYW1lcyhzdi5hbHBoYSkgPSBiLmdyYXBoLm5hbWVzCiAgc3YuYWxwaGFbbmFtZXMoc3YuYWxwaGEpICVpbiUgbmFtZXMobm9kZXMuY250LmFjYyldID0gMQogIAogICMgc2V0LnNlZWQoMjM5KQogICMgcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAjICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAjICAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiAgIyAgICAgICAgICAgICBhbHBoYSA9IHN2LmFscGhhLAogICMgICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAjICAgICAgICAgICAgICMgYXJyb3cuZ2FwID0gMCwgCiAgIyAgICAgICAgICAgICAjIGFycm93LnNpemUgPSAzLAogICMgICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgCiAgc2V0LnNlZWQoMjApCiAgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5zbWFsbCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLAogICAgICAgICAgICBhbHBoYSA9IHN2LmFscGhhW2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwKICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgogIHBkZihwYXN0ZShwYXRoLmZpZ3VyZXMuYWNjLCAnZ3JhcGhfdGUnLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19zbWFsbF9hY2NfJyxhY2MsJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKICBwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgogIGRldi5vZmYoKQogIAogIAogIHNldC5zZWVkKDIwKQogIHAgPC0gZ2duZXQyKGcucGFydC5zdWIuYmlnLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwKICAgICAgICAgICAgYWxwaGEgPSBzdi5hbHBoYVtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLAogICAgICAgICAgICBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgogIHBkZihwYXN0ZShwYXRoLmZpZ3VyZXMuYWNjLCAnZ3JhcGhfdGUnLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19iaWdfYWNjXycsYWNjLCcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCiAgcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKICBkZXYub2ZmKCkKCn0KCnAgCgoKCmBgYAoKCmBgYHtyfQpzdi5hbm5vdCA9IHJlYWQudGFibGUoJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29ya19zdi9zdnNfYW5ub3RhdGlvbl92MDMudHh0Jywgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnJvd25hbWVzKHN2LmFubm90KSA9IHN2LmFubm90JGdyCmhlYWQoc3YuYW5ub3QpCgpzdi5hbm5vdFtleHRyYWN0ZWRfdmFsdWVzLF0KCmBgYAoKCgoKIyBTdG9wCmBgYHtyfQpzdG9wKCkKYGBgCgoKIyBCaWcgVEUtbm9kZXMKYGBge3J9Cm4uYW1vdW50ID0gMjAKCmcucGFydCA8LSBuZXR3b3JrKGIuZ3JhcGgsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0KQoKc2l6ZS5iaWcgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXQphbHBoYS5iaWcgPSByZXAoMSwgbGVuZ3RoKGIuZ3JhcGgubmFtZXMpKQpuYW1lcyhhbHBoYS5iaWcpID0gYi5ncmFwaC5uYW1lcwphbHBoYS5iaWdbc2l6ZS5iaWcgPCBuLmFtb3VudF0gPSAwCgpzdW0oc2l6ZS5iaWcgPj0gbi5hbW91bnQpCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gc2l6ZS5iaWcsIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBhbHBoYT0gYWxwaGEuYmlnLAogICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICAjIGFycm93LmdhcCA9IDAsIAogICAgICAgICAgICAjIGFycm93LnNpemUgPSAzLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpCnAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9zbWFsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfZmFtaWx5X2Ftb3VudC5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocCsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKCgoKYGBgCgojIyBXaGljaCBmYW1pbGllcyBzcGVjaWZpY2FsbHksIGFuZCBpcyB0aGUgcmF0ZSBvZiBpbnNlcnRpb24gaXMgZGlmZmVyZW50Pwpjb21wYXJlIG51bWJlciBvZiBpbnNlcnRpb25zIHdpdGggdGhlIHRvdGFsIG51bWJlciBvZiBURSBsb2FkCmBgYHtyfQoKYmlnLmZhbWlsaWVzID0gZGF0YS5mcmFtZShub2RlID0gIG5hbWVzKHNpemUuYmlnKVtzaXplLmJpZyA+PSBuLmFtb3VudF0pCmJpZy5mYW1pbGllcyRzaXplID0gc2l6ZS5iaWdbYmlnLmZhbWlsaWVzJG5vZGVdCmJpZy5mYW1pbGllcyRmYW0gPSBnLm5vZGVzLmZhbVtiaWcuZmFtaWxpZXMkbm9kZV0KYmlnLmZhbWlsaWVzID0gYmlnLmZhbWlsaWVzW29yZGVyKC1iaWcuZmFtaWxpZXMkc2l6ZSksXQpyb3duYW1lcyhiaWcuZmFtaWxpZXMpID0gTlVMTAoKbm9kZS5iaWcgPSBub2Rlc1tub2RlcyRub2RlICVpbiUgYmlnLmZhbWlsaWVzJG5vZGUsXQoKdiA9IHJlYWQudGFibGUocGFzdGUocGF0aC53b3JrLCAnYmxhc3Rfc3Zfb25fdGVzLnR4dCcsIHNlcCA9ICcnKSkKdiA9IHZbdiRWMSAlaW4lIG5vZGUuYmlnJHRlLF0KCgpwb3MubGVuMSA9IDIKcG9zLmxlbjIgPSA1CnYxLmxlbiA9IHNhcHBseSh1bmlxdWUodiRWMSksIGZ1bmN0aW9uKHMpIGFzLm51bWVyaWMoc3Ryc3BsaXQocywnXFx8JylbWzFdXVtwb3MubGVuMV0pKQp2OC5sZW4gPSBzYXBwbHkodW5pcXVlKHYkVjgpLCBmdW5jdGlvbihzKSBhcy5udW1lcmljKHN0cnNwbGl0KHMsJ1xcfCcpW1sxXV1bcG9zLmxlbjJdKSkKdi5sZW4gPSBjKHYxLmxlbiwgdjgubGVuKQoKdi5zaW0gPSBmaW5kTmVzdGVkbmVzcyh2LCB1c2Uuc3RyYW5kID0gRikKCnYuc2ltID0gZmluZE5lc3RlZG5lc3ModiwgdXNlLnN0cmFuZCA9IEYpCnYuc2ltJHAxID0gdi5zaW0kQzEgLyB2Lmxlblt2LnNpbSRWMV0Kdi5zaW0kcDggPSB2LnNpbSRDOCAvIHYubGVuW3Yuc2ltJFY4XQp2LnNpbSRwMS5pbjggPSB2LnNpbSRDMSAvIHYubGVuW3Yuc2ltJFY4XQp2LnNpbSRwOC5pbjEgPSB2LnNpbSRDOCAvIHYubGVuW3Yuc2ltJFYxXQoKbm9kZS5iaWckc3ViZmFtID0gJycKZm9yKHN2Lm5hbWUgaW4gdW5pcXVlKHYuc2ltJFYxKSl7CiAgdi50bXAgPSB2LnNpbVt2LnNpbSRWMSA9PSBzdi5uYW1lLF0KICBzID0gdi50bXAkVjhbd2hpY2gubWF4KHYudG1wJHAxKV0KICBzID0gc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bOF0KICBub2RlLmJpZ1tzdi5uYW1lLCAnc3ViZmFtJ10gPSBzCn0KCgp4ID0gdGFwcGx5KG5vZGUuYmlnJHN1YmZhbSwgbm9kZS5iaWckbm9kZSwgZnVuY3Rpb24oeCl7CiAgY250ID0gdGFibGUoeCkKICB4ID0gbmFtZXMoY250KVtjbnQgPT0gbWF4KGNudCldCiAgcmV0dXJuKHBhc3RlMCh4LCBjb2xsYXBzZSA9ICAnLCcpKQp9KQoKYmlnLmZhbWlsaWVzJHN1YmZhbSA9IHhbYmlnLmZhbWlsaWVzJG5vZGVdCgoKYGBgCgoKCiMgbm8tVEUgU1YKIyMgQ29uc3RydWN0CmBgYHtyfQpzdi5zZSA9IHJlYWRSRFMocGFzdGUocGF0aC5zdnMsICdzdl9zZS5yZHMnLCBzZXAgPSAnJykpCnNpbS5jdXRvZmYgPSAwLjg1CgoKc3Yuc2Uubm8udGUgPSBzdi5zZSRuYW1lWyhzdi5zZSR0ZSA9PSAnbm9URScpICYgKHN2LnNlJGxlbiA+IDUwKV0KCmJsLmZpbGUgPSBwYXN0ZShwYXRoLndvcmssJ3N2X2JpZ19vbl9iaWcudHh0Jywgc2VwID0gJycpCmJsLnN2ID0gcmVhZC50YWJsZShibC5maWxlLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKYmwuc3YgPSBibC5zdltibC5zdiRWMSAhPSBibC5zdiRWOCxdCgojIHJlbW92ZSBoYXZpbmcgVEVzCmJsLnN2ID0gYmwuc3ZbYmwuc3YkVjEgJWluJSBzdi5zZS5uby50ZSwgXQpibC5zdiA9IGJsLnN2W2JsLnN2JFY4ICVpbiUgc3Yuc2Uubm8udGUsIF0KCnBvcy5sZW4xID0gMgpzdi5sZW4gPSBzYXBwbHkodW5pcXVlKGMoYmwuc3YkVjEsIGJsLnN2JFY4KSksIGZ1bmN0aW9uKHMpIGFzLm51bWVyaWMoc3Ryc3BsaXQocywnXFx8JylbWzFdXVtwb3MubGVuMV0pKQpibC5zdiRsZW4xID0gc3YubGVuW2JsLnN2JFYxXQpibC5zdiRsZW44ID0gc3YubGVuW2JsLnN2JFY4XQptYXgubGVuID0gMjAwMDAKYmwuc3YgPSBibC5zdlsoYmwuc3YkbGVuMSA8PSBtYXgubGVuKSAmIChibC5zdiRsZW44IDw9IG1heC5sZW4pLF0KYmwuc3YkcDEgPSAoYmwuc3YkVjMgLSBibC5zdiRWMiArIDEpIC8gYmwuc3YkbGVuMQpibC5zdiRwOCA9IChhYnMoYmwuc3YkVjUgLSBibC5zdiRWNCkgKyAxKSAvIGJsLnN2JGxlbjgKYmwuc3YkY29tYiA9IGFzLmZhY3RvcihwYXN0ZShibC5zdiRWMSwgYmwuc3YkVjgsIHNlcCA9ICd8fCcpKQoKaWR4Lm11dHVhbCA9IChibC5zdiRwMSA+PSBzaW0uY3V0b2ZmKSAmIChibC5zdiRwOCA+PSBzaW0uY3V0b2ZmKQojIFRoZXJlIGlzIGEgYmlnIGRpc2N1c3Npb24gaW4gbXkgaGVhZCwgd2hldGhlciBpdCBzaG91bGQgYmUgJyYnIG9yICd8JwojIElmIGl0J3Mgbm90ICx1dHVhbCwgdGhlbiBtYXliZSB3aXRoIHNvbWV0aGluZyBlbHNlIGl0IHdpbGwgY29uc3RydWN0IGEgbXV0dWFsIHJlbGF0aW9uLCAKIyBzbyB3ZSBzaG91bGQgcmVtYWluIGZvciB0aGUgYW5hbHlzaXMgb2YgbmVzdGVkbmVzcyBhbGwgcGFydGlhbCBpbmNsdXNpb25zCnN2Lm11dHVhbCA9IGJsLnN2W2lkeC5tdXR1YWwsIF0KdiA9IGJsLnN2WyFpZHgubXV0dWFsLCBdCnYgPSB2WyEodiRjb21iICVpbiUgc3YubXV0dWFsJGNvbWIpLF0KCiMgQXQgc29tZSBwb2ludCBpdCB3YXMgYSBzdGVwIHRvIHJlbWFpbiBvbmx5IHRob3NlIGluc3RhbmNlcyB3aGljaCBhcmUgbm90ICJ1bmlxdWUiIGluIGNvbWJpbmF0aW9ucwojIGJ1dCBJIHRoaW5rIGl0J3Mgbm90IGNvcnJlY3QgaGVyZQoKc3Yuc2ltID0gZmluZE5lc3RlZG5lc3ModiwgdXNlLnN0cmFuZCA9IFQpCnN2LnNpbSRwMSA9IHN2LnNpbSRDMSAvIHN2Lmxlbltzdi5zaW0kVjFdCnN2LnNpbSRwOCA9IHN2LnNpbSRDOCAvIHN2Lmxlbltzdi5zaW0kVjhdCgojIGhlcmUgIHdlIHNob3VsZCBmaW5hbGx5IHVzZSAnfCcsIG5vdCAnJicKc3YubmVzdGVkID0gc3Yuc2ltWyhzdi5zaW0kcDEgPj0gc2ltLmN1dG9mZikgfCAoc3Yuc2ltJHA4ID49IHNpbS5jdXRvZmYpICxdCgojIENyZWF0ZSBwcmUtZGF0YSBmb3IgZGVmaW5pbmcgZWRnZXMKY29tbW9uLm5hbWVzID0gaW50ZXJzZWN0KGNvbG5hbWVzKHN2Lm11dHVhbCksIGNvbG5hbWVzKHN2Lm5lc3RlZCkpCnN2Lm92ZXJhbGwgPSByYmluZChzdi5tdXR1YWxbLGNvbW1vbi5uYW1lc10sIHN2Lm5lc3RlZFssY29tbW9uLm5hbWVzXSkKc3Yub3ZlcmFsbCRncm91cCA9IChzdi5vdmVyYWxsJHAxID49IHNpbS5jdXRvZmYpICogMSArIChzdi5vdmVyYWxsJHA4ID49IHNpbS5jdXRvZmYpICogMgppZHgxID0gc3Yub3ZlcmFsbCRncm91cCAhPSAyICAjIFYxIGluIFY4CmlkeDIgPSBzdi5vdmVyYWxsJGdyb3VwICE9IDEgICMgVjggaW4gVjEKCgojIEVkZ2VzIApzdi5lZGdlcyA9IHJiaW5kKGNiaW5kKHN2Lm92ZXJhbGwkVjFbaWR4MV0sIHN2Lm92ZXJhbGwkVjhbaWR4MV0pLAogICAgICAgICAgICAgICAgIGNiaW5kKHN2Lm92ZXJhbGwkVjhbaWR4Ml0sIHN2Lm92ZXJhbGwkVjFbaWR4Ml0pKQoKCnN2LmdyYXBoIDwtIGlncmFwaDo6bWFrZV9ncmFwaCh0KHN2LmVkZ2VzKSwgZGlyZWN0ZWQgPSBUKQpzdi5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHN2LmdyYXBoKQpzdi5ncmFwaGNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHN2LmdyYXBoKQoKc3YubWVtYiA9IGRhdGEuZnJhbWUobWVtYiA9IHN2LmdyYXBoY29tcCRtZW1iZXJzaGlwKQpzdi5tZW1iJG5hbWUgPSByb3duYW1lcyhzdi5tZW1iKQpyb3duYW1lcyhzdi5tZW1iKSA9IE5VTEwKcm93bmFtZXMoc3Yuc2UpID0gc3Yuc2UkbmFtZQpzdi5tZW1iJHRlID0gc3Yuc2Vbc3YubWVtYiRuYW1lLCAndGUnXQpzdi5tZW1iJGNvdmVyID0gc3Yuc2Vbc3YubWVtYiRuYW1lLCAnY292ZXInXSAvIHN2LnNlW3N2Lm1lbWIkbmFtZSwgJ2xlbiddCnN2Lm1lbWIkbGVuID0gc3YubGVuW3N2Lm1lbWIkbmFtZV0KYGBgCgojIyBQbG90IGFsbApgYGB7cn0KZy5wYXJ0IDwtIG5ldHdvcmsoc3YuZWRnZXMsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0KQoKc2V0LnNlZWQoMjM5KQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSAxLAogICAgICAgICAgICAjIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAgICAgICAgICAgIyBjb2xvciA9IGcubm9kZXMudHlwZVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgIyBwYWxldHRlID0gZy5jb2xzCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgpgYGAKCiMjIFBsb3Qgd2l0aCBjb2xvcnMKYGBge3J9CnN2LnByb3QuaW5pdCA9IHJlYWRSRFMocGFzdGUocGF0aC53b3JrLCAnc3ZfcHJvdGVpbnNfbm9fdGVfYmxhc3QucmRzJywgc2VwID0gJycpKQpzdi5wcm90LmluaXQkbmFtZSA9IHNhcHBseShzdi5wcm90LmluaXQkWDEsIGZ1bmN0aW9uKHMpewogIHMgPSBwYXN0ZTAoc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bMToyXSwgY29sbGFwc2UgPSAnfCcpCiAgcmV0dXJuKHMpCn0pCnN2LnByb3QgPSBzdi5wcm90LmluaXRbc3YucHJvdC5pbml0JHByb3QgPT0gMSxdCnN2LnByb3RbLDJdID0gdG9sb3dlcihzdi5wcm90WywyXSkKCnR5cGVzID0gYygnZGlzZWFzZScsICdyZXBlYXQnLCAncmVjZXB0b3InLCAgJ3ppbmMnLCAndHJhbnNjcmlwdGFzZScsICdyZXZlcnNlJywgJ3RyYW5zcG9zJykKZm9yKGkudHlwZSBpbiAxOmxlbmd0aCh0eXBlcykpewogIHN2LnByb3RbLHR5cGVzW2kudHlwZV1dID0gKGdyZXBsKHR5cGVzW2kudHlwZV0sIHN2LnByb3RbLDJdKSkgKiAxCn0Kc3YucHJvdCR0eXBlID0gcm93U3Vtcyhzdi5wcm90Wyx0eXBlc10pCnRhYmxlKHN2LnByb3QkdHlwZSkKCgoKc3YubWVtYiRwcm90ID0gJ25vIHByb3QnCnN2Lm1lbWIkcHJvdFtzdi5tZW1iJG5hbWUgJWluJSBzdi5wcm90LmluaXQkbmFtZV0gPSAndW5kZWZpbmVkIHByb3QnCnN2Lm1lbWIkcHJvdFtzdi5tZW1iJG5hbWUgJWluJSBzdi5wcm90JG5hbWVdID0gJ2RlZmluZWQgcHJvdCcKZm9yKHR5cGUgaW4gdHlwZXMpewogIHN2Lm1lbWIkcHJvdFtzdi5tZW1iJG5hbWUgJWluJSBzdi5wcm90JG5hbWVbc3YucHJvdFssdHlwZV0gPT0gMV1dID0gdHlwZQp9CgpnLm5vZGVzLnByb3QgPSBzdi5tZW1iJHByb3QKZy5ub2Rlcy5wcm90W2cubm9kZXMucHJvdCA9PSAnZGlzZWFzZSddID0gJ2RlZmluZWQgcHJvdCcKbmFtZXMoZy5ub2Rlcy5wcm90KSA9IHN2Lm1lbWIkbmFtZQoKZy5jb2xzID0gZGlzY3JldGVfcmFpbmJvdyhsZW5ndGgodW5pcXVlKHN2Lm1lbWIkcHJvdCkpKQpuYW1lcyhnLmNvbHMpID0gdW5pcXVlKHN2Lm1lbWIkcHJvdCkKIyBuYW1lcyhnLmNvbHMpID0gYygnbm8gcHJvdCcsICJ1bmRlZmluZWQgcHJvdCIsICJyZXZlcnNlIiwgInRyYW5zcG9zIiwicmVwZWF0IiwiemluYyIsICJyZWNlcHRvciIsImRlZmluZWQgcHJvdCIpCgpnLmNvbHNbJ2Rpc2Vhc2UnXSA9ICdibGFjaycKIyBnLmNvbHNbJ2RlZmluZWQgcHJvdCddID0gJyNGMEI4NkUnCiMgZy5jb2xzWydyZXBlYXQnXSA9ICcjQ0FFMEFCJwojIGcuY29sc1snemluayddID0gJyMxQTVEMUEnCiMgZy5jb2xzWydyZWNlcHRvciddID0gJyNFRDdCN0InCgpnLmNvbHNbJ3ppbmMnXSA9ICcjRjQ1MDUwJwpnLmNvbHNbJ3JlcGVhdCddID0gJyNFQTkwNkMnCmcuY29sc1sncmVjZXB0b3InXSA9ICcjRjdEMDYwJwoKZy5jb2xzWydyZXZlcnNlJ10gPSAnIzk4RDhBQScKZy5jb2xzWydkZWZpbmVkIHByb3QnXSA9ICcjN0JBRkRFJwoKCgpzZXQuc2VlZCgyMzkpCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICMgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBub2RlLnNpemUgPSAxLAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMucHJvdFtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scywKICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgogICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKSArIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBnLmNvbHMsIAogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoInRyYW5zcG9zIiwicmV2ZXJzZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVwZWF0IiwiemluYyIsInJlY2VwdG9yIiwgImRlZmluZWQgcHJvdCIsICJ1bmRlZmluZWQgcHJvdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibm8gcHJvdCIpLCAKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdQcm90ZWluIGtleS13b3JkOicpICsgdGhlbWUobGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDEsIDApKQpwID0gcCsgdGhlbWUobGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuNSwgImNtIikpCnAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9uZXdfYWxsLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCiMgCiMgY250ID0gdGFibGUoZy5ub2Rlcy5wcm90KQojIGNudCA9IGMoc3VtKGNudFtjKCJ0cmFuc3BvcyIsInJldmVyc2UiLCJyZXBlYXQiLCJ6aW5jIildKSwgc3VtKGNudFtjKCJyZWNlcHRvciIsImRlZmluZWQgcHJvdCIpXSksCiMgICAgICAgICBjbnRbInVuZGVmaW5lZCBwcm90Il0sIGNudFsibm8gcHJvdCJdKQoKYGBgCgojIyBUeXBlcyBvZiB0aGUgY29tcG9uZW50CgoKYGBge3J9Cgpzdi5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChzdi5lZGdlcyksIGRpcmVjdGVkID0gVCkKc3YuZ3JhcGggPC0gaWdyYXBoOjpzaW1wbGlmeShzdi5ncmFwaCkKc3YuZ3JhcGhjb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyhzdi5ncmFwaCkKCnN2LmNvbXAubWVtYmVyID0gc3YuZ3JhcGhjb21wJG1lbWJlcnNoaXAKCnMudGFncyA9IGMoInRyYW5zcG9zIiwicmV2ZXJzZSIsInJlcGVhdCIsInppbmMiLCAicmVjZXB0b3IiLCJkZWZpbmVkIHByb3QiLCAidW5kZWZpbmVkIHByb3QiLCAnbm8gcHJvdCcpCnMudGFnczAgPSByZXAoJycsIGxlbmd0aChzLnRhZ3MpKQpzLnRhZ3MwWzE6NF0gPSAnVEUtbGlrZScKcy50YWdzMFs1OjZdID0gJ0tub3duIFByb3RlaW5zJwpzLnRhZ3MwWzddID0gJ1VuZGVmLiBQcm90ZWlucycKcy50YWdzMFs4XSA9ICdObyBQcm90ZWlucycKbmFtZXMocy50YWdzMCkgPSBzLnRhZ3MKCmNvbXAudGFncyA9IHJlcCgnJywgbGVuZ3RoKHVuaXF1ZShzdi5jb21wLm1lbWJlcikpKQpmb3Iocy50YWcgaW4gcy50YWdzKXsKICB0bXAudGFncyA9IHVuaXF1ZShzdi5jb21wLm1lbWJlcltuYW1lcyhnLm5vZGVzLnByb3QpW2cubm9kZXMucHJvdCA9PSBzLnRhZ11dKQogIGNvbXAudGFnc1t0bXAudGFnc11bY29tcC50YWdzW3RtcC50YWdzXSA9PSAnJ10gPSBzLnRhZwp9CmNvbXAudGFnc1tjb21wLnRhZ3MgPT0gJyddID0gJ25vIHByb3QnCmNvbXAudGFncyA9IGRhdGEuZnJhbWUodGFibGUoY29tcC50YWdzKSkKY29sbmFtZXMoY29tcC50YWdzKSA9IGMoJ3RhZzEnLCAnZnJlcScpCmNvbXAudGFncyR0YWcxID0gZmFjdG9yKGNvbXAudGFncyR0YWcxLCBsZXZlbHMgPSBzLnRhZ3MpCmNvbXAudGFncyA9IGNvbXAudGFnc1tvcmRlcihjb21wLnRhZ3MkdGFnMSksXQoKY29tcC50YWdzJHRhZzAgPSBzLnRhZ3MwW2NvbXAudGFncyR0YWcxXQpjb21wLnRhZ3MkdGFnMCA9IGZhY3Rvcihjb21wLnRhZ3MkdGFnMCwgbGV2ZWxzID0gdW5pcXVlKHMudGFnczApKQoKeS50aWNrcyA9IHRhcHBseShjb21wLnRhZ3MkZnJlcSwgY29tcC50YWdzJHRhZzAsIHN1bSkKeS50aWNrcyA9IHkudGlja3NbIWlzLm5hKHkudGlja3MpXQoKeXkgPSBzdW0oeS50aWNrcykgLSBjdW1zdW0oeS50aWNrcykgKyB5LnRpY2tzLzIKCmNvbXAudGFncyR5bWluIDwtIGMoMCwgY3Vtc3VtKGNvbXAudGFncyRmcmVxKVstbGVuZ3RoKGNvbXAudGFncyRmcmVxKV0pCmNvbXAudGFncyR5bWF4IDwtIGN1bXN1bShjb21wLnRhZ3MkZnJlcSkKCnguc3RlcCA9IHJlcCgwLCA4KQpuLnN0ZXAgPSAxMAp4LnN0ZXBbYyg1LDcsOCldID0gbi5zdGVwCnguc3RlcCA9IGN1bXN1bSh4LnN0ZXApCgpjb21wLnRhZ3MkeW1pbiA9IGNvbXAudGFncyR5bWluICsgeC5zdGVwCmNvbXAudGFncyR5bWF4ID0gY29tcC50YWdzJHltYXggKyB4LnN0ZXAKCnkubWluID0gdGFwcGx5KGNvbXAudGFncyR5bWluLCBjb21wLnRhZ3MkdGFnMCwgbWluKQp5Lm1heCA9IHRhcHBseShjb21wLnRhZ3MkeW1heCwgY29tcC50YWdzJHRhZzAsIG1heCkKeS52YWwgPSAoeS5tYXggKyB5Lm1pbikgLyAyCnkuY250ID0gdGFwcGx5KGNvbXAudGFncyRmcmVxLCBjb21wLnRhZ3MkdGFnMCwgc3VtKQoKZGYudGV4dCA9IGRhdGEuZnJhbWUoeS5taW4gPSB5Lm1pbiwgeS5tYXggPSB5Lm1heCwgeS52YWwgPSB5LnZhbCwgeS5jbnQgPSB5LmNudCwgbGFiZWwgPSBuYW1lcyh5LnZhbCkpCmRmLnRleHQkYW5nbGVzIDwtIDM2MCAtIChkZi50ZXh0JHkudmFsIC8gKG1heChjb21wLnRhZ3MkeW1heCkgKyBuLnN0ZXApKSAqIDM2MCAKZGYudGV4dCRhbmdsZXNbMjozXSA9IDE4MCArIGRmLnRleHQkYW5nbGVzWzI6M10KCnAgPSBnZ3Bsb3QoY29tcC50YWdzLCBhZXMoeCA9IDAsIHkgPSBmcmVxLCBmaWxsID0gdGFnMSkpICsKICAgZ2VvbV9yZWN0KGFlcyh4bWluID0gLTAuNSwgeG1heCA9IDAuNSwgeW1pbiA9IHltaW4sIHltYXggPSB5bWF4KSkgKwogICBjb29yZF9wb2xhcigieSIsIHN0YXJ0ID0gMCkgKwogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBnLmNvbHMucGx1cykgKyB5bGltKDAsIG1heChjb21wLnRhZ3MkeW1heCkgKyBuLnN0ZXApICsKICAgdGhlbWVfdm9pZCgpICsgeGxpbSgtMS41LCAwLjcpICsgCiAgIGdlb21fdGV4dChkYXRhPWRmLnRleHQsIGFlcyh4ID0gMC43LCB5ID0geS52YWwsIGxhYmVsID0gcGFzdGUobGFiZWwsIHkuY250LCBzZXAgPSAnOiAnKSksIAogICAgICAgICAgICAgYW5nbGUgPSBkZi50ZXh0JGFuZ2xlcywgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gLTEuNSwgeSA9IDAsIGxhYmVsID0gcGFzdGUoJ1RvdGFsJyxzdW0oY29tcC50YWdzJGZyZXEpLCdcbiBjb25uZWN0ZWQgXG5jb21wb25lbnRzJykpIAoKcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX25ld19waWVfY2hhcnQucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDMuMSwgaGVpZ2h0ID0gMy4xKQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCmBgYAoKCgojIyMjIEkgZG9uJ3Qga25vdwpgYGB7cn0Kc3Yuc2UkZnJlcSA9IHN2LnNlJGZyZXEubWF4Cm4uY3V0b2ZmID0gMwpuID0gMjgKc3Yuc2Ukc2luID0gJ2luZGVsJwpzdi5zZSRzaW5bc3Yuc2UkZnJlcSA+PSAobiAtIG4uY3V0b2ZmKV0gPSAnZGVsZXRpb24nCnN2LnNlJHNpbltzdi5zZSRmcmVxIDw9IG4uY3V0b2ZmXSA9ICdpbnNlcnRpb24nCgoKZy5ub2Rlcy5wcm90LnNpbiA9IGcubm9kZXMucHJvdApnLm5vZGVzLnByb3Quc2luW25hbWVzKGcubm9kZXMucHJvdC5zaW4pICVpbiUgc3Yuc2UkbmFtZVtzdi5zZSRzaW4gIT0gJ2luc2VydGlvbiddIF0gPSAnbmEnCmcuY29sc1snbmEnXSA9ICd3aGl0ZScKCgoKCnNldC5zZWVkKDIzOSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90LnNpbltiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scywKICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgogICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKQpwIAoKIyAKIyBwYXRoLmZpZ3VyZXMgID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29ya190ZS8nCiMgcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX3N2X25vdGVfaW5zZXJ0aW9uLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQojIHByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCiMgZGV2Lm9mZigpCgoKYWxwaGEuZWR0YSA9IHJlcCgxLCBsZW5ndGgoYi5ncmFwaC5uYW1lcykpCm5hbWVzKGFscGhhLmVkdGEpID0gYi5ncmFwaC5uYW1lcwoKc3YuYW5ub3QuYWR0YSA9IHJvd1N1bXMoc3YuYW5ub3RbLDExOm5jb2woc3YuYW5ub3QpXSA+IDAuNykgPiAwCnN2LmFubm90LmFkdGEgPSBzdi5hbm5vdC5hZHRhW3N2LnNlJGdyXQpuYW1lcyhzdi5hbm5vdC5hZHRhKSA9IHN2LnNlJG5hbWUKc3YuYW5ub3QuYWR0YSA9IHN2LmFubm90LmFkdGFbc3YuYW5ub3QuYWR0YV0KYWxwaGEuZWR0YVtuYW1lcyhhbHBoYS5lZHRhKSAlaW4lIG5hbWVzKHN2LmFubm90LmFkdGEpXSA9IDAKCgpzZXQuc2VlZCgyMzkpCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICMgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBub2RlLnNpemUgPSAxLAogICAgICAgICAgICBhbHBoYT0xLWFscGhhLmVkdGEsCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9ub3RlX2VkdGEucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKcGF0aC5maWd1cmVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvJwpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX25vdGVfZWR0YV9ub19sZWdlbmQucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgoKIyMgUGxvdCB3aXRoIGNvbXBvbmVudCBJRApgYGB7cn0KCgp0bXAuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoc3YuZWRnZXMpLCBkaXJlY3RlZCA9IFQpCnRtcC5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRtcC5ncmFwaCkKdG1wLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRtcC5ncmFwaCkKCnNpemUubGltaXQgPSA1CmNvbXAuaWQgPSBhcy5jaGFyYWN0ZXIodG1wLmNvbXAkbWVtYmVyc2hpcCkKbmFtZXMoY29tcC5pZCkgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKQpjb21wLmlkW3RtcC5jb21wJGNzaXplW3RtcC5jb21wJG1lbWJlcnNoaXBdIDwgc2l6ZS5saW1pdF0gPSAnJwoKbmFtZXMudGUgPSBuYW1lcyhnLm5vZGVzLnByb3QpW2cubm9kZXMucHJvdCAlaW4lIGMoJ3RyYW5zcG9zJywgJ3JldmVyc2UnKV0KCmNvbXAuaWRbIShuYW1lcyhjb21wLmlkKSAlaW4lIG5hbWVzLnRlKV0gPSAnJwoKY29tcC5pZFtkdXBsaWNhdGVkKGNvbXAuaWQpXSA9ICcnCgoKY29tcC5yZW1haW4gPSBhcy5udW1lcmljKGNvbXAuaWRbY29tcC5pZCAhPSAnJ10pCmFscGhhID0gcmVwKDAsIGxlbmd0aChiLmdyYXBoLm5hbWVzKSkKbmFtZXMoYWxwaGEpID0gbmFtZXModG1wLmNvbXAkbWVtYmVyc2hpcCkKYWxwaGFbdG1wLmNvbXAkbWVtYmVyc2hpcCAlaW4lIGNvbXAucmVtYWluXSA9IDEKCnNldC5zZWVkKDIzOSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IGNvbXAuaWRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBsYWJlbC5jb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgIGxhYmVsLnNpemUgPSAzLAogICAgICAgICAgICBlZGdlLmNvbG9yID0gImdyZXkiLCAKICAgICAgICAgICAgYWxwaGEgPSBhbHBoYVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgoKcGF0aC5maWd1cmVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvJwpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfc3Zfbm90ZV9udW1iZXJzLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKCiMgT3JkZXIgb2YgY29tcG9uZW50cwpjbnQgPSB0YWJsZSh0bXAuY29tcCRtZW1iZXJzaGlwW3RtcC5jb21wJG1lbWJlcnNoaXAgJWluJSBjb21wLnJlbWFpbl0pCmNudCA9IGNudFtvcmRlcigtY250KV0KCmBgYAoKIyMgQ05WCmBgYHtyfQoKY252ID0gcmVhZFJEUygnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3N2L3NpbWlsYXJfY252X3N2X29uX2FjY2Vzc2lvbnNfY3VtXzAuOS5yZHMnKQoKYGBgCgojIyBQbG90IG9uZSBzcGVjaWZpYyBuZXR3b3JrCmBgYHtyfQoKcGF0aC5maWd1cmVzLmV4YW1wbGVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvZXhhbXBsZXMvJwoKIyAKIyB0bXAuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoc3YuZWRnZXMpLCBkaXJlY3RlZCA9IFQpCiMgdG1wLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodG1wLmdyYXBoKQojIHRtcC5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0bXAuZ3JhcGgpCiMgCiMgdG1wLmNudCA9IHRhYmxlKHRtcC5jb21wJG1lbWJlcnNoaXApCiMgdG1wLmNudCA9IC1zb3J0KC10bXAuY250KQoKdG1wLmNudCA9IGNudAoKZm9yKGsgaW4gMTpsZW5ndGgodG1wLmNudCkpewogIHRtcC5rID0gYXMubnVtZXJpYyhuYW1lcyh0bXAuY250KVtrXSkKICB0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwID09IHRtcC5rXQogIGIuZ3JhcGguc3ViID0gc3YuZWRnZXNbKHN2LmVkZ2VzWywxXSAlaW4lIHRtcC5uYW1lcykgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAoc3YuZWRnZXNbLDJdICVpbiUgdG1wLm5hbWVzKSxdCiAgCiAgCiAgZy5wYXJ0LnN1YiA8LSBuZXR3b3JrKGIuZ3JhcGguc3ViLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKICBiLmdyYXBoLm5hbWVzLnN1YiA9IG5ldHdvcmsudmVydGV4Lm5hbWVzKGcucGFydC5zdWIpCiAgCiAgCiAgICAKICBiLmdyYXBoLnNpemUuc3ViIDwtIGFzLm51bWVyaWMoc3ViKCIuKlxcfCIsICIiLCBiLmdyYXBoLm5hbWVzLnN1YikpCiAgbmFtZXMoYi5ncmFwaC5zaXplLnN1YikgPSBiLmdyYXBoLm5hbWVzLnN1YgogICMgYi5ncmFwaC5zaXplLnN1YiA9IGNlaWxpbmcobG9nKGIuZ3JhcGguc2l6ZS5zdWIsIDEwKSkKICAKICBpZigobGVuZ3RoKHVuaXF1ZSggZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXMuc3ViXSkpID09IDEpKXsKICAgIHNldC5zZWVkKDIwKQogICAgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1YiwgbGFiZWwgPSBiLmdyYXBoLnNpemUuc3ViW2IuZ3JhcGgubmFtZXMuc3ViXSwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgbm9kZS5zaXplID0gMTUsCiAgICAgICAgICAgICAgICBhcnJvdy5nYXAgPSAwLjA3LCBhcnJvdy5zaXplID0gMywKICAgICAgICAgICAgICAgIGNvbG9yID0gZy5jb2xzW2cubm9kZXMucHJvdFtiLmdyYXBoLm5hbWVzLnN1Yl1bMV1dLAogICAgICAgICAgICAgICAgKSArIGd1aWRlcyhzaXplID0gRikgKyAgZ2d0aXRsZShwYXN0ZSgnQ29tcG9uZW50ICMnLCB0bXAuaykpCiAgICBwCiAgfSBlbHNlIHsKICAgIHNldC5zZWVkKDIwKQogICAgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1YiwgbGFiZWwgPSBiLmdyYXBoLnNpemUuc3ViW2IuZ3JhcGgubmFtZXMuc3ViXSwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgbm9kZS5zaXplID0gMTUsCiAgICAgICAgICAgICAgICBhcnJvdy5nYXAgPSAwLjA3LCBhcnJvdy5zaXplID0gMywKICAgICAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXMuc3ViXSwKICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBnLmNvbHMsCiAgICAgICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKSArICBnZ3RpdGxlKHBhc3RlKCdDb21wb25lbnQgIycsIHRtcC5rKSkKICAgIHAKICB9CiAgCiAKICAKICBwZGYocGFzdGUocGF0aC5maWd1cmVzLmV4YW1wbGVzLCAnZ3JhcGhfc3ZfZXhhbXBsZV8nLGssJ19jb21wXycsdG1wLmssJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNCkKICBwcmludChwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKICBkZXYub2ZmKCkKICAKICAjIGFubm90YXRpb24KICBhbm5vdC50bXAgPSBzdi5wcm90W3N2LnByb3QkbmFtZSAlaW4lIGIuZ3JhcGgubmFtZXMuc3ViLF0KICAjIGFubm90LnRtcCA9IGFubm90LnRtcFthbm5vdC50bXAkdHJhbnNwb3MgPT0gMSxdCiAgCiAgd3JpdGUudGFibGUoYW5ub3QudG1wLCBwYXN0ZShwYXRoLmZpZ3VyZXMuZXhhbXBsZXMsICdncmFwaF9zdl9leGFtcGxlXycsaywnX3BibGFzdC50eHQnLCBzZXAgPSAnJyksIAogICAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsIGNvbC5uYW1lcyA9IEYsIHF1b3RlID0gRiwgc2VwID0gJ1x0JykKICAKICAKICAjIGlmIEVEVEEgYW5ub3RhdGlvbiBleGlzdHMKICBzdi50bXAgPSB1bmlxdWUoYyhiLmdyYXBoLnN1YikpCiAgc3YudG1wLmN1dCA8LSBnc3ViKCJcXHwuKiIsICIiLCBzdi50bXApCiAgc3YuYW5ub3QudG1wID0gc3YuYW5ub3Rbc3YudG1wLmN1dCxdCiAgbi5maXggPSA5CiAgc3YuYW5ub3QudG1wICA9IHN2LmFubm90LnRtcFssYygxOm4uZml4LG4uZml4K3doaWNoKGNvbFN1bXMoc3YuYW5ub3QudG1wWywobi5maXgrMSk6bmNvbChzdi5hbm5vdC50bXApXSkgIT0gMCkpXQogIHJvd25hbWVzKHN2LmFubm90LnRtcCkgPSBzdi50bXAKICAgIAogIHdyaXRlLnRhYmxlKHN2LmFubm90LnRtcCwgcGFzdGUocGF0aC5maWd1cmVzLmV4YW1wbGVzLCAnZ3JhcGhfc3ZfZXhhbXBsZV8nLGssJ19lZHRhLnR4dCcsIHNlcCA9ICcnKSwgCiAgICAgICAgICAgICByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYsIHNlcCA9ICdcdCcpCiAgCiAgIyBDb3B5ME51bWJlciB2YXJpYXRpb24KICBjbnYudG1wID0gY252W3N2LnRtcCxdCiAgCiAgaGVhdG1hcChjbnYudG1wLCBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoIndoaXRlIiwgInJlZCIpKSgyMCkpCiAgCn0KCmBgYAojIFBpZS1jaGFydCBvZiBwcm90ZWlucwpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKZGF0YSA8LSBkYXRhLmZyYW1lKAogIHR5cGUgPSBjKCJubyBwcm90ZWlucyIsICJURS1yZWxhdGVkIiwgItCa0LDRgtC10LPQvtGA0LjRjyAyIiwgItCa0LDRgtC10LPQvtGA0LjRjyAzIiwgItCa0LDRgtC10LPQvtGA0LjRjyA0IiksCiAgdmFsdWUgPSBjKDEzNSwgNjMsIDg1LCAxMzMpCikKCnBpZS5jaGFydCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSAiIiwgeSA9IHZhbHVlLCBmaWxsID0gdHlwZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArCiAgY29vcmRfcG9sYXIoInkiLCBzdGFydCA9IDApICsKICB0aGVtZV92b2lkKCkKCnBpZS5jaGFydAoKYGBgCgojIyBBZG1peHR1cmUgZ3JvdXBzCmBgYHtyfQpncm91cHMgPC0gYygKICAiZ2VybWFueSIsCiAgInNvdXRoX3N3ZWRlbiIsCiAgIm5vcnRoX3N3ZWRlbiIsCiAgInNvdXRoX3N3ZWRlbiIsCiAgIm5vcnRoX3N3ZWRlbiIsCiAgImdlcm1hbnkiLAogICJ3ZXN0ZXJuX2V1cm9wZSIsCiAgImNlbnRyYWxfZXVyb3BlIiwKICAiaXRhbHlfYmFsa2FuX2NhdWNhc3VzIiwKICAic3BhaW4iLAogICJyZWxpY3QiLAogICJhc2lhIiwKICAiY2VudHJhbF9ldXJvcGUiLAogICJhZG1peGVkIiwKICAic3BhaW4iLAogICJyZWxpY3QiLAogICJpdGFseV9iYWxrYW5fY2F1Y2FzdXMiLAogICJ3ZXN0ZXJuX2V1cm9wZSIsCiAgImFzaWEiLAogICJhZnJpY2EiLAogICJjaGluYSIsCiAgImNoaW5hIiwKICAiYWZyaWNhIiwKICAiYWZyaWNhIiwKICAibWFkZWlyYSIsCiAgIm1hZGVpcmEiLAogICJhZnJpY2EiCikKCiMg0JjRgdC/0L7Qu9GM0LfRg9C10Lwg0YTRg9C90LrRhtC40Y4gdGFibGUoKSDQtNC70Y8g0L/QvtC00YHRh9C10YLQsCDQutC+0LvQuNGH0LXRgdGC0LLQsCDRjdC70LXQvNC10L3RgtC+0LIg0LIg0LrQsNC20LTQvtC5INCz0YDRg9C/0L/QtQphcy5tYXRyaXgodGFibGUoZ3JvdXBzKSkKYGBgCgoKCiMgT0xECmBgYHtyfQpzdW5zZXQgPC0gY29sb3VyKCJzdW5zZXQiKQpkaXNjcmV0ZV9yYWluYm93IDwtIGNvbG91cigiZGlzY3JldGUgcmFpbmJvdyIpCgpmaWxlLnRlID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29yay9ibGFzdF90ZXNfYW5uLnR4dCcKc2ltLmN1dG9mZiA9IDAuODUKbGVuLmN1dG9mZiA9IDEwMApgYGAKCgpgYGB7cn0KCmIgPSByZWFkLnRhYmxlKGZpbGUudGUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpiID0gYltiJFYxICE9IGIkVjgsXQpiJGxlbjEgPSBhcy5udW1lcmljKHNhcHBseShiJFYxLCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs3XSkpCmIkbGVuMiA9IGFzLm51bWVyaWMoc2FwcGx5KGIkVjgsIGZ1bmN0aW9uKHMpIHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzddKSkKYiA9IGJbYiRsZW4xID49IGxlbi5jdXRvZmYsXQpiID0gYltiJGxlbjIgPj0gbGVuLmN1dG9mZixdCmIkY29tYiA9IHBhc3RlKGIkVjEsIGIkVjgsIHNlcCA9ICdeJykKCiMgT3JkZXIgcG9zaXRpb25zIGluIGJhc2UKaWR4ID0gYiRWNCA+IGIkVjUKdG1wID0gYltpZHgsICdWNCddCmJbaWR4LCAnVjQnXSA9IGJbaWR4LCAnVjUnXQpiW2lkeCwgJ1Y1J10gPSB0bXAKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBHZXQgc2VwYXJhdGVseSB0aG9zZSwgd2hvIGhhcyBhIHVuaXF1ZSBjb3ZlcmFnZQpjb21iLnRibCA9IHRhYmxlKGIkY29tYikKaWR4LnVuaSA9IGIkY29tYiAlaW4lIG5hbWVzKGNvbWIudGJsKVtjb21iLnRibCA9PSAxXQpiLnVuaSA9IGJbaWR4LnVuaSxdCmIgPSBiWyFpZHgudW5pLF0KCiMgVGhpcyB2YXJpYWJsZSB3aWxsIGJlIHVzZWQgbGF0ZXIKYi51bmkkcDEgPSAoYi51bmkkVjMgLSBiLnVuaSRWMiArIDEpIC8gYi51bmkkbGVuMQpiLnVuaSRwMiA9IChiLnVuaSRWNSAtIGIudW5pJFY0ICsgMSkgLyBiLnVuaSRsZW4yCmIudW5pID0gYi51bmlbKGIudW5pJHAxID49IHNpbS5jdXRvZmYpIHwgKGIudW5pJHAyID49IHNpbS5jdXRvZmYpLF0KCmIucmVsYXRpb25zID0gZGF0YS5mcmFtZShzdWIudGUgPSBiLnVuaSRWMVtiLnVuaSRwMSA+PSBzaW0uY3V0b2ZmXSwKICAgICAgICAgICAgICAgICAgICAgICAgIHRlID0gYi51bmkkVjhbYi51bmkkcDEgPj0gc2ltLmN1dG9mZl0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpiLnJlbGF0aW9ucyA9IHJiaW5kKGIucmVsYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoc3ViLnRlID0gYi51bmkkVjhbYi51bmkkcDIgPj0gc2ltLmN1dG9mZl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZSA9IGIudW5pJFYxW2IudW5pJHAyID49IHNpbS5jdXRvZmZdLCBzdHJpbmdzQXNGYWN0b3JzID0gRikpCmIucmVsYXRpb25zID0gdW5pcXVlKGIucmVsYXRpb25zKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIE1pbi1tYXggb2YgdGhlIGNvdmVyYWdlIHRvIHJlbW92ZSB0aG9zZSwgd2hvIGFyZSBOT1QgaW4gZWFjaCBvdGhlciBjb21wbGV0ZWx5CmIuY292ID0gdGFwcGx5KGIkVjIsIGIkY29tYiwgbWluKQpiLmNvdiA9IGRhdGEuZnJhbWUoY29tYiA9IG5hbWVzKGIuY292KSwgVjIgPSBiLmNvdikKYi5jb3YkVjMgPSB0YXBwbHkoYiRWMywgYiRjb21iLCBtYXgpCmIuY292JFY0ID0gdGFwcGx5KGIkVjQsIGIkY29tYiwgbWluKQpiLmNvdiRWNSA9IHRhcHBseShiJFY1LCBiJGNvbWIsIG1heCkKYi5jb3YkbGVuMSA9IHRhcHBseShiJGxlbjEsIGIkY29tYiwgdW5pcXVlKQpiLmNvdiRsZW4yID0gdGFwcGx5KGIkbGVuMiwgYiRjb21iLCB1bmlxdWUpCmIuY292JHAxID0gKGIuY292JFYzIC0gYi5jb3YkVjIgKyAxKSAvIGIuY292JGxlbjEKYi5jb3YkcDIgPSAoYi5jb3YkVjUgLSBiLmNvdiRWNCArIDEpIC8gYi5jb3YkbGVuMgoKY29tYi51bmNvdiA9IGIuY292JGNvbWJbKGIuY292JHAxIDwgc2ltLmN1dG9mZikgJiAoYi5jb3YkcDIgPCBzaW0uY3V0b2ZmKV0KCmIgPSBiWyEoYiRjb21iICVpbiUgY29tYi51bmNvdiksXQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENhbGN1bGF0ZSB0aGUgY292ZXJhZ2UgZGlyZWN0bHkgZm9yIHRoZSBmaXJzdApiID0gYltvcmRlcihiJFYzKSxdCmIgPSBiW29yZGVyKGIkVjIpLF0KYiA9IGJbb3JkZXIoYiRjb21iKSxdCgojIFJlbW92ZSBuZXN0ZWQKaWR4ID0gd2hpY2goKGIkVjNbLW5yb3coYildID4gYiRWM1stMV0pICYgKGIkY29tYlstbnJvdyhiKV0gPT0gYiRjb21iWy0xXSkpICsgMQpiMSA9IGJbLWlkeCxdCgojIENvbXB1dGUgZ2FwcwpiMSRnYXAgPSBjKGIxJFYyWy0xXSAtIGIxJFYzWy1ucm93KGIxKV0gLSAxLCAwKQpiMSRnYXBbYjEkZ2FwIDwgMF0gPSAwCmlkeC5kaWZmLmNvbWIgPSB3aGljaChiMSRjb21iWy0xXSAhPSBiMSRjb21iWy1ucm93KGIxKV0pCmIxJGdhcFtpZHguZGlmZi5jb21iXSA9IDAKCmIuY292ID0gdGFwcGx5KGIxJFYyLCBiMSRjb21iLCBtaW4pCmIuY292ID0gZGF0YS5mcmFtZShjb21iID0gbmFtZXMoYi5jb3YpLCBWMiA9IGIuY292KQpiLmNvdiRWMyA9IHRhcHBseShiMSRWMywgYjEkY29tYiwgbWF4KQpiLmNvdiRsZW4xID0gdGFwcGx5KGIxJGxlbjEsIGIxJGNvbWIsIHVuaXF1ZSkKYi5jb3YkZ2FwID0gdGFwcGx5KGIxJGdhcCwgYjEkY29tYiwgc3VtKQpiLmNvdiRsZW4xID0gYi5jb3YkbGVuMSAKYi5jb3YkcDEgPSAoYi5jb3YkVjMgLSBiLmNvdiRWMiArIDEgLSBiLmNvdiRnYXApIC8gYi5jb3YkbGVuMQpiLmNvdiRWMSA9IHRhcHBseShiMSRWMSwgYjEkY29tYiwgdW5pcXVlKQpiLmNvdiRWOCA9IHRhcHBseShiMSRWOCwgYjEkY29tYiwgdW5pcXVlKQoKYi5jb3YgPSBiLmNvdltiLmNvdiRwMSA+PSBzaW0uY3V0b2ZmLF0KCgpiLnJlbGF0aW9ucyA9IHJiaW5kKGIucmVsYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoc3ViLnRlID0gYi5jb3YkVjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZSA9IGIuY292JFY4LCBzdHJpbmdzQXNGYWN0b3JzID0gRikpCgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENhbGN1bGF0ZSB0aGUgY292ZXJhZ2UgZGlyZWN0bHkgZm9yIHRoZSBzZWNvbmQKYiA9IGJbb3JkZXIoYiRWNSksXQpiID0gYltvcmRlcihiJFY0KSxdCmIgPSBiW29yZGVyKGIkY29tYiksXQoKIyBSZW1vdmUgbmVzdGVkCmlkeCA9IHdoaWNoKChiJFY1Wy1ucm93KGIpXSA+IGIkVjVbLTFdKSAmIChiJGNvbWJbLW5yb3coYildID09IGIkY29tYlstMV0pKSArIDEKYjEgPSBiWy1pZHgsXQoKIyBDb21wdXRlIGdhcHMKYjEkZ2FwID0gYyhiMSRWNFstMV0gLSBiMSRWNVstbnJvdyhiMSldIC0gMSwgMCkKYjEkZ2FwW2IxJGdhcCA8IDBdID0gMAppZHguZGlmZi5jb21iID0gd2hpY2goYjEkY29tYlstMV0gIT0gYjEkY29tYlstbnJvdyhiMSldKQpiMSRnYXBbaWR4LmRpZmYuY29tYl0gPSAwCgpiLmNvdiA9IHRhcHBseShiMSRWNCwgYjEkY29tYiwgbWluKQpiLmNvdiA9IGRhdGEuZnJhbWUoY29tYiA9IG5hbWVzKGIuY292KSwgVjQgPSBiLmNvdikKYi5jb3YkVjUgPSB0YXBwbHkoYjEkVjUsIGIxJGNvbWIsIG1heCkKYi5jb3YkbGVuMiA9IHRhcHBseShiMSRsZW4yLCBiMSRjb21iLCB1bmlxdWUpCmIuY292JGdhcCA9IHRhcHBseShiMSRnYXAsIGIxJGNvbWIsIHN1bSkKYi5jb3YkbGVuMiA9IGIuY292JGxlbjIgCmIuY292JHAxID0gKGIuY292JFY1IC0gYi5jb3YkVjQgKyAxIC0gYi5jb3YkZ2FwKSAvIGIuY292JGxlbjIKYi5jb3YkVjEgPSB0YXBwbHkoYjEkVjEsIGIxJGNvbWIsIHVuaXF1ZSkKYi5jb3YkVjggPSB0YXBwbHkoYjEkVjgsIGIxJGNvbWIsIHVuaXF1ZSkKCmIuY292ID0gYi5jb3ZbYi5jb3YkcDEgPj0gc2ltLmN1dG9mZixdCgoKYi5yZWxhdGlvbnMgPSByYmluZChiLnJlbGF0aW9ucywKICAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKHN1Yi50ZSA9IGIuY292JFY4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGUgPSBiLmNvdiRWMSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpKQoKICAKYi5yZWxhdGlvbnMgPSB1bmlxdWUoYi5yZWxhdGlvbnMpCgoKYi5yZWxhdGlvbnMKCmBgYAoKCiMgRGVmaW5lIGNsdXN0ZXJzCmBgYHtyfQpiLm5vZGVzID0gcmJpbmQoYi5yZWxhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZShzdWIudGUgPSBiLnJlbGF0aW9ucyR0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlID0gYi5yZWxhdGlvbnMkc3ViLnRlKSkKCmIubm9kZXMkY29tYiA9IHBhc3RlKGIubm9kZXMkc3ViLnRlLCBiLm5vZGVzJHRlLCBzZXAgPSAnXicpCgpjb21iLnRibCA9IHRhYmxlKGIubm9kZXMkY29tYikKY29tYi5iYWNrLmFuZC5mb3RoID0gbmFtZXMoY29tYi50YmwpW2NvbWIudGJsID49IDJdCmIubm9kZXMgPSBiLm5vZGVzW2Iubm9kZXMkY29tYiAlaW4lIGNvbWIuYmFjay5hbmQuZm90aCxdCmIubm9kZXMgPSB1bmlxdWUoYi5ub2Rlc1ssIGMoJ3N1Yi50ZScsICd0ZScpXSkKCgp0ZS5ub2RlcyA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChiLm5vZGVzKSwgZGlyZWN0ZWQgPSBUKQp0ZS5ub2RlcyA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLm5vZGVzKQp0ZS5ub2Rlcy5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0ZS5ub2RlcykKCm5vZGVzID0gcGFzdGUoJ04nLCB0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXAsIHNlcCA9ICcnKQpuYW1lcyhub2RlcykgPSBuYW1lcyh0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXApCmBgYAoKIyMgSWRlbnRpZnkgZmFtaWx5IGZvciBlYWNoIG5vZGUKYGBge3J9Cgpub2Rlcy5mYW1pbHkgPSBzYXBwbHkobmFtZXMobm9kZXMpLCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs2XSkKCm5vZGVzLmZhbWlseS5tYXggPSB0YXBwbHkobm9kZXMuZmFtaWx5LCBub2RlcywgZnVuY3Rpb24ocyl7CiAgdGJsID0gdGFibGUocykKICBmID0gbmFtZXModGJsKVt0YmwgPT0gbWF4KHRibCldCiAgaWYobGVuZ3RoKGYpID09IDEpewogICAgcmV0dXJuKGYpCiAgfSBlbHNlIHsKICAgIHJldHVybignTWl4JykKICB9Cn0pCgpub2Rlcy5mYW1pbHkubWF4W25vZGVzLmZhbWlseS5tYXggJWluJSBjKCdETkEvUG9nbycsICdETkEvVGMxJywgJ0ROQS9IYXJiaW5nZXInLCAnRE5BL0VuLVNwbScsCiAgICAgICAgICAgICAgICAgICAgICdETkEvSEFUJywgJ0ROQScsICdETkEvTWFyaW5lcicpXSA9ICdETkEnCm5vZGVzLmZhbWlseS5tYXhbbm9kZXMuZmFtaWx5Lm1heCAlaW4lIGMoJ1JhdGhFMV9jb25zJywgJ1JhdGhFMl9jb25zJyldID0gJ0ROQScKbm9kZXMuZmFtaWx5Lm1heFtub2Rlcy5mYW1pbHkubWF4ICVpbiUgYygnTElORS9MMScsICdMSU5FPycpXSA9ICdMSU5FJwpub2Rlcy5mYW1pbHkubWF4W25vZGVzLmZhbWlseS5tYXggJWluJSBjKCdVbmFzc2lnbmVkJyldID0gJ01peCcKbm9kZXMuZmFtaWx5LnVuaXF1ZSA9IHVuaXF1ZShub2Rlcy5mYW1pbHkubWF4KQoKCgpgYGAKCgojIyBHcmFwaCB3aXRob3V0IHNpbmdsZXRvbnMKYGBge3J9CgpiLmdyYXBoLmluaXQgPSBiLnJlbGF0aW9uc1soYi5yZWxhdGlvbnMkc3ViLnRlICVpbiUgbmFtZXMobm9kZXMpKSAmIChiLnJlbGF0aW9ucyR0ZSAlaW4lIG5hbWVzKG5vZGVzKSksXQpiLmdyYXBoID0gYi5ncmFwaC5pbml0CmIuZ3JhcGggPSBjYmluZChub2Rlc1thcy5jaGFyYWN0ZXIoYi5ncmFwaCRzdWIudGUpXSwgbm9kZXNbYXMuY2hhcmFjdGVyKGIuZ3JhcGgkdGUpXSkKYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoKQoKCmIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KCiMgcmVkdWNlIGluZGlyZWN0IGFycm93cwppZHgucmVtb3ZlID0gYygpCmZvcihpLmVkZ2UgaW4gMTpucm93KGIuZ3JhcGgpKXsKICBpZihpLmVkZ2UgJSUgMTAwMCA9PSAwKSBwcmludChpLmVkZ2UpCiAgdG1wLnRvID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoW2kuZWRnZSwxXSwyXQogIHRtcC5mcm9tID0gYi5ncmFwaFtiLmdyYXBoWywyXSA9PSBiLmdyYXBoW2kuZWRnZSwyXSwxXQogIGlmKGxlbmd0aChpbnRlcnNlY3QodG1wLnRvLCB0bXAuZnJvbSkpID4gMCkgaWR4LnJlbW92ZSA9IGMoaWR4LnJlbW92ZSwgaS5lZGdlKQp9CmlkeC5yZW1vdmUgPSB1bmlxdWUoaWR4LnJlbW92ZSkKYi5ncmFwaCA9IGIuZ3JhcGhbLWlkeC5yZW1vdmUsXQoKCiMgdGUuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoYi5ncmFwaCksIGRpcmVjdGVkID0gVCkKIyB0ZS5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLmdyYXBoKQojIHRlLmdyYXBoLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRlLmdyYXBoKQoKCm5vZGVzLmZhbWlseS5tYXguZ3JhcGggPSBub2Rlcy5mYW1pbHkubWF4W25hbWVzKG5vZGVzLmZhbWlseS5tYXgpICVpbiUgdW5pcXVlKGMoYi5ncmFwaFssMV0sIGIuZ3JhcGhbLDJdKSldCgpncmFwaC5jb2xzID0gc3Vuc2V0KGxlbmd0aCh1bmlxdWUobm9kZXMuZmFtaWx5Lm1heC5ncmFwaCkpKQoKZ3JhcGguY29scyA9IGRpc2NyZXRlX3JhaW5ib3cobGVuZ3RoKHVuaXF1ZShub2Rlcy5mYW1pbHkubWF4LmdyYXBoKSkpCm5hbWVzKGdyYXBoLmNvbHMpID0gdW5pcXVlKG5vZGVzLmZhbWlseS5tYXguZ3JhcGgpCmcucGFydCA8LSBuZXR3b3JrKGIuZ3JhcGgsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRkFMU0UsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCBub2RlLnNpemUgPSAxLCAKICAgICAgICAgICAgY29sb3IgPSBub2Rlcy5mYW1pbHkubWF4LmdyYXBoLCBwYWxldHRlID0gZ3JhcGguY29scywKICAgICAgICAgICAgbW9kZSA9ICJrYW1hZGFrYXdhaSIpIyArIGd1aWRlcyhzaXplID0gRkFMU0UpCnAKCmBgYAojIyBHcmFwaCBXSVRIIHNpbmdsZXRvbnMKYGBge3J9CgoKbmFtZXMuY29yZSA9IG5hbWVzKG5vZGVzLmZhbWlseS5tYXguZ3JhcGgpCgpiLmdyYXBoLmluaXQgPSBiLnJlbGF0aW9ucwpmb3IoaSBpbiAxOjIpewogIGIuZ3JhcGguaW5pdFtiLmdyYXBoLmluaXRbLGldICVpbiUgbmFtZXMobm9kZXMpLCBpXSA9IG5vZGVzW2IuZ3JhcGguaW5pdFtiLmdyYXBoLmluaXRbLGldICVpbiUgbmFtZXMobm9kZXMpLCBpXV0KfQoKYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoLmluaXQpCmIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoKQojIFZlcnRlY2VzIGZyb20gdGhlIHByZXZpb3VzIGdyYXBoCmIuZ3JhcGggPSBiLmdyYXBoWyhiLmdyYXBoWywxXSAlaW4lIG5hbWVzLmNvcmUpIHwgKGIuZ3JhcGhbLDJdICVpbiUgbmFtZXMuY29yZSksXQoKCiMgcmVkdWNlIGluZGlyZWN0IGFycm93cwppZHgucmVtb3ZlID0gYygpCmZvcihpLmVkZ2UgaW4gMTpucm93KGIuZ3JhcGgpKXsKICBpZihpLmVkZ2UgJSUgMTAwMCA9PSAwKSBwcmludChpLmVkZ2UpCiAgdG1wLnRvID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoW2kuZWRnZSwxXSwyXQogIHRtcC5mcm9tID0gYi5ncmFwaFtiLmdyYXBoWywyXSA9PSBiLmdyYXBoW2kuZWRnZSwyXSwxXQogIGlmKGxlbmd0aChpbnRlcnNlY3QodG1wLnRvLCB0bXAuZnJvbSkpID4gMCkgaWR4LnJlbW92ZSA9IGMoaWR4LnJlbW92ZSwgaS5lZGdlKQp9CmlkeC5yZW1vdmUgPSB1bmlxdWUoaWR4LnJlbW92ZSkKYi5ncmFwaCA9IGIuZ3JhcGhbLWlkeC5yZW1vdmUsXQoKdGUuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoYi5ncmFwaCksIGRpcmVjdGVkID0gVCkKZCA8LSBpZ3JhcGg6OmRpc3RhbmNlcyh0ZS5ncmFwaCkKIyB0ZS5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLmdyYXBoKQojIHRlLmdyYXBoLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRlLmdyYXBoKQoKbmFtZXMubmV3ID0gdW5pcXVlKHNldGRpZmYoYyhiLmdyYXBoWywxXSwgYi5ncmFwaFssMl0pLCBuYW1lcyhub2Rlcy5mYW1pbHkubWF4KSkpCiMgbmFtZXMubmV3LnZhbCA9IHBhc3RlKCdHJywxOmxlbmd0aChuYW1lcy5uZXcpLCBzZXAgPSAnJykKIyBuYW1lcyhuYW1lcy5uZXcudmFsKSA9IG5hbWVzLm5ldwojIG5hbWVzLm5ldy52YWwgPSAKCm5hbWVzLm5ldy5mYW1pbHkgPSBzYXBwbHkobmFtZXMubmV3LCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs2XSkKbmFtZXMubmV3LmZhbWlseVtuYW1lcy5uZXcuZmFtaWx5ICVpbiUgYygnRE5BL1BvZ28nLCAnRE5BL1RjMScsICdETkEvSGFyYmluZ2VyJywgJ0ROQS9Fbi1TcG0nLAogICAgICAgICAgICAgICAgICAgICAnRE5BL0hBVCcsICdETkEnLCAnRE5BL01hcmluZXInKV0gPSAnRE5BJwpuYW1lcy5uZXcuZmFtaWx5W25hbWVzLm5ldy5mYW1pbHkgJWluJSBjKCdSYXRoRTFfY29ucycsICdSYXRoRTJfY29ucycpXSA9ICdETkEnCm5hbWVzLm5ldy5mYW1pbHlbbmFtZXMubmV3LmZhbWlseSAlaW4lIGMoJ0xJTkUvTDEnLCAnTElORT8nKV0gPSAnTElORScKbmFtZXMubmV3LmZhbWlseVtuYW1lcy5uZXcuZmFtaWx5ICVpbiUgYygnVW5hc3NpZ25lZCcpXSA9ICdNaXgnCgoKbm9kZXMuZmFtaWx5Lm1heC5hZGQgPSBjKG5vZGVzLmZhbWlseS5tYXgsIG5hbWVzLm5ldy5mYW1pbHkpCm5vZGVzLmZhbWlseS5tYXguYWRkID0gbm9kZXMuZmFtaWx5Lm1heC5hZGRbdW5pcXVlKGMoYi5ncmFwaFssMV0sIGIuZ3JhcGhbLDJdKSldCgpncmFwaC5jb2xzID0gZGlzY3JldGVfcmFpbmJvdyhsZW5ndGgodW5pcXVlKG5vZGVzLmZhbWlseS5tYXguYWRkKSkpCmdyYXBoLmNvbHMgPSBzYW1wbGUoZ3JhcGguY29scykKbmFtZXMoZ3JhcGguY29scykgPSB1bmlxdWUobm9kZXMuZmFtaWx5Lm1heC5hZGQpCgpnLnBhcnQgPC0gbmV0d29yayhiLmdyYXBoLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEZBTFNFLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgbm9kZS5zaXplID0gMC41LCAKICAgICAgICAgICAgY29sb3IgPSBub2Rlcy5mYW1pbHkubWF4LmFkZCwKICAgICAgICAgICAgcGFsZXR0ZSA9IGdyYXBoLmNvbHMsIG1vZGUgPSAia2FtYWRha2F3YWkiKQpwCmBgYAoKIyBUU05FCmBgYHtyfQoKCmxpYnJhcnkoUnRzbmUpCgoKCgpkIDwtIGlncmFwaDo6ZGlzdGFuY2VzKHRlLmdyYXBoKQpkLm1heCA9IG1heChkWyFpcy5pbmZpbml0ZShkKV0pCgpkW2lzLmluZmluaXRlKGQpXSA9IGQubWF4ICogMS4zCgp0U05FIDwtIFJ0c25lKGQsIGlzX2Rpc3RhbmNlID0gVFJVRSwgZGltcyA9IDIpCgpwbG90KHRTTkUkWVssMV0sIHRTTkUkWVssMl0pCgpgYGAKCgoK